1 // ****************************************************************
\r
2 // This is free software licensed under the NUnit license. You
\r
3 // may obtain a copy of the license as well as information regarding
\r
4 // copyright ownership at http://nunit.org/?p=license&r=2.4.
\r
5 // ****************************************************************
\r
11 using System.Text.RegularExpressions;
\r
12 using System.Reflection;
\r
15 /// The TestMethod class represents a TestCase implemented as a method
\r
16 /// call on a fixture object. At the moment, this is the only way we
\r
17 /// implement a TestCase, but others are expected in the future.
\r
19 /// Because of how exceptions are handled internally, this class
\r
20 /// must incorporate processing of expected exceptions. A change to
\r
21 /// the TestCase interface might make it easier to process exceptions
\r
22 /// in an object that aggregates a TestMethod in the future.
\r
24 public abstract class TestMethod : TestCase
\r
30 private MethodInfo method;
\r
33 /// The SetUp method.
\r
35 protected MethodInfo setUpMethod;
\r
38 /// The teardown method
\r
40 protected MethodInfo tearDownMethod;
\r
43 /// The exception handler method
\r
45 internal MethodInfo exceptionHandler;
\r
48 /// True if an exception is expected
\r
50 internal bool exceptionExpected;
\r
53 /// The type of any expected exception
\r
55 internal Type expectedExceptionType;
\r
58 /// The full name of any expected exception type
\r
60 internal string expectedExceptionName;
\r
63 /// The value of any message associated with an expected exception
\r
65 internal string expectedMessage;
\r
68 /// A string indicating how to match the expected message
\r
70 internal string matchType;
\r
73 /// A string containing any user message specified for the expected exception
\r
75 internal string userMessage;
\r
79 #region Constructors
\r
80 public TestMethod( MethodInfo method )
\r
83 this.method = method;
\r
88 public MethodInfo Method
\r
90 get { return method; }
\r
93 public bool ExceptionExpected
\r
95 get { return exceptionExpected; }
\r
96 set { exceptionExpected = value; }
\r
99 public MethodInfo ExceptionHandler
\r
101 get { return exceptionHandler; }
\r
102 set { exceptionHandler = value; }
\r
105 public Type ExpectedExceptionType
\r
107 get { return expectedExceptionType; }
\r
110 expectedExceptionType = value;
\r
111 expectedExceptionName = expectedExceptionType != null
\r
112 ? expectedExceptionType.FullName
\r
117 public string ExpectedExceptionName
\r
119 get { return expectedExceptionName; }
\r
122 expectedExceptionType = null;
\r
123 expectedExceptionName = value;
\r
127 public string ExpectedMessage
\r
129 get { return expectedMessage; }
\r
130 set { expectedMessage = value; }
\r
133 public string MatchType
\r
135 get { return matchType; }
\r
136 set { matchType = value; }
\r
139 public string UserMessage
\r
141 get { return userMessage; }
\r
142 set { userMessage = value; }
\r
146 #region Run Methods
\r
147 public override void Run(TestCaseResult testResult)
\r
151 if ( this.Parent != null)
\r
152 Fixture = this.Parent.Fixture;
\r
154 if (!testResult.IsFailure)
\r
156 // Temporary... to allow for tests that directly execute a test case
\r
157 if (Fixture == null)
\r
158 Fixture = Reflect.Construct(this.FixtureType);
\r
160 if (this.Properties["_SETCULTURE"] != null)
\r
161 TestContext.CurrentCulture =
\r
162 new System.Globalization.CultureInfo((string)Properties["_SETCULTURE"]);
\r
167 catch (Exception ex)
\r
169 if (ex is NUnitException)
\r
170 ex = ex.InnerException;
\r
172 RecordException(ex, testResult);
\r
181 /// The doRun method is used to run a test internally.
\r
182 /// It assumes that the caller is taking care of any
\r
183 /// TestFixtureSetUp and TestFixtureTearDown needed.
\r
185 /// <param name="testResult">The result in which to record success or failure</param>
\r
186 public virtual void doRun( TestCaseResult testResult )
\r
188 DateTime start = DateTime.Now;
\r
192 if ( setUpMethod != null )
\r
193 Reflect.InvokeMethod( setUpMethod, this.Fixture );
\r
195 doTestCase( testResult );
\r
197 catch(Exception ex)
\r
199 if ( ex is NUnitException )
\r
200 ex = ex.InnerException;
\r
202 RecordException( ex, testResult );
\r
206 doTearDown( testResult );
\r
208 DateTime stop = DateTime.Now;
\r
209 TimeSpan span = stop.Subtract(start);
\r
210 testResult.Time = (double)span.Ticks / (double)TimeSpan.TicksPerSecond;
\r
215 #region Invoke Methods by Reflection, Recording Errors
\r
217 private void doTearDown( TestCaseResult testResult )
\r
221 if ( tearDownMethod != null )
\r
222 tearDownMethod.Invoke( this.Fixture, new object[0] );
\r
224 catch(Exception ex)
\r
226 if ( ex is NUnitException )
\r
227 ex = ex.InnerException;
\r
228 // TODO: What about ignore exceptions in teardown?
\r
229 testResult.Error( ex,FailureSite.TearDown );
\r
233 private void doTestCase( TestCaseResult testResult )
\r
237 RunTestMethod(testResult);
\r
238 ProcessNoException(testResult);
\r
240 catch( Exception ex )
\r
242 if ( ex is NUnitException )
\r
243 ex = ex.InnerException;
\r
245 if ( IsIgnoreException( ex ) )
\r
246 testResult.Ignore( ex );
\r
248 ProcessException(ex, testResult);
\r
252 public virtual void RunTestMethod(TestCaseResult testResult)
\r
254 Reflect.InvokeMethod( this.method, this.Fixture );
\r
259 #region Record Info About An Exception
\r
261 protected void RecordException( Exception ex, TestResult testResult )
\r
263 if ( IsIgnoreException( ex ) )
\r
264 testResult.Ignore( ex.Message );
\r
265 else if ( IsAssertException( ex ) )
\r
266 testResult.Failure( ex.Message, ex.StackTrace );
\r
268 testResult.Error( ex );
\r
271 protected string GetStackTrace(Exception exception)
\r
275 return exception.StackTrace;
\r
279 return "No stack trace available";
\r
285 #region Exception Processing
\r
286 protected internal virtual void ProcessNoException(TestCaseResult testResult)
\r
288 if ( ExceptionExpected )
\r
289 testResult.Failure(NoExceptionMessage(), null);
\r
291 testResult.Success();
\r
294 protected internal virtual void ProcessException(Exception exception, TestCaseResult testResult)
\r
296 if (!ExceptionExpected)
\r
298 RecordException(exception, testResult);
\r
302 if (IsExpectedExceptionType(exception))
\r
304 if (IsExpectedMessageMatch(exception))
\r
306 if ( exceptionHandler != null )
\r
307 Reflect.InvokeMethod( exceptionHandler, this.Fixture, exception );
\r
309 testResult.Success();
\r
313 testResult.Failure(WrongTextMessage(exception), GetStackTrace(exception));
\r
316 else if (IsAssertException(exception))
\r
318 testResult.Failure(exception.Message, exception.StackTrace);
\r
322 testResult.Failure(WrongTypeMessage(exception), GetStackTrace(exception));
\r
327 #region Abstract Methods
\r
328 protected abstract bool IsAssertException(Exception ex);
\r
330 protected abstract bool IsIgnoreException(Exception ex);
\r
333 #region Helper Methods
\r
334 protected bool IsExpectedExceptionType(Exception exception)
\r
336 return expectedExceptionName == null || expectedExceptionName.Equals(exception.GetType().FullName);
\r
339 protected bool IsExpectedMessageMatch(Exception exception)
\r
341 if (expectedMessage == null)
\r
348 return expectedMessage.Equals(exception.Message);
\r
350 return exception.Message.IndexOf(expectedMessage) >= 0;
\r
352 return Regex.IsMatch(exception.Message, expectedMessage);
\r
356 protected string NoExceptionMessage()
\r
358 string expectedType = expectedExceptionName == null ? "An Exception" : expectedExceptionName;
\r
359 return CombineWithUserMessage( expectedType + " was expected" );
\r
362 protected string WrongTypeMessage(Exception exception)
\r
364 return CombineWithUserMessage(
\r
365 "An unexpected exception type was thrown" + Environment.NewLine +
\r
366 "Expected: " + expectedExceptionName + Environment.NewLine +
\r
367 " but was: " + exception.GetType().FullName + " : " + exception.Message );
\r
370 protected string WrongTextMessage(Exception exception)
\r
372 string expectedText;
\r
377 expectedText = "Expected: ";
\r
380 expectedText = "Expected message containing: ";
\r
383 expectedText = "Expected message matching: ";
\r
387 return CombineWithUserMessage(
\r
388 "The exception message text was incorrect" + Environment.NewLine +
\r
389 expectedText + expectedMessage + Environment.NewLine +
\r
390 " but was: " + exception.Message );
\r
393 private string CombineWithUserMessage( string message )
\r
395 if ( userMessage == null )
\r
397 return userMessage + Environment.NewLine + message;
\r