1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
|
// ****************************************************************
// This is free software licensed under the NUnit license. You
// may obtain a copy of the license as well as information regarding
// copyright ownership at http://nunit.org/?p=license&r=2.4.
// ****************************************************************
namespace NUnit.Core
{
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Reflection;
/// <summary>
/// The TestMethod class represents a TestCase implemented as a method
/// call on a fixture object. At the moment, this is the only way we
/// implement a TestCase, but others are expected in the future.
///
/// Because of how exceptions are handled internally, this class
/// must incorporate processing of expected exceptions. A change to
/// the TestCase interface might make it easier to process exceptions
/// in an object that aggregates a TestMethod in the future.
/// </summary>
public abstract class TestMethod : TestCase
{
#region Fields
/// <summary>
/// The test method
/// </summary>
private MethodInfo method;
/// <summary>
/// The SetUp method.
/// </summary>
protected MethodInfo setUpMethod;
/// <summary>
/// The teardown method
/// </summary>
protected MethodInfo tearDownMethod;
/// <summary>
/// The exception handler method
/// </summary>
internal MethodInfo exceptionHandler;
/// <summary>
/// True if an exception is expected
/// </summary>
internal bool exceptionExpected;
/// <summary>
/// The type of any expected exception
/// </summary>
internal Type expectedExceptionType;
/// <summary>
/// The full name of any expected exception type
/// </summary>
internal string expectedExceptionName;
/// <summary>
/// The value of any message associated with an expected exception
/// </summary>
internal string expectedMessage;
/// <summary>
/// A string indicating how to match the expected message
/// </summary>
internal string matchType;
/// <summary>
/// A string containing any user message specified for the expected exception
/// </summary>
internal string userMessage;
#endregion
#region Constructors
public TestMethod( MethodInfo method )
: base( method )
{
this.method = method;
}
#endregion
#region Properties
public MethodInfo Method
{
get { return method; }
}
public bool ExceptionExpected
{
get { return exceptionExpected; }
set { exceptionExpected = value; }
}
public MethodInfo ExceptionHandler
{
get { return exceptionHandler; }
set { exceptionHandler = value; }
}
public Type ExpectedExceptionType
{
get { return expectedExceptionType; }
set
{
expectedExceptionType = value;
expectedExceptionName = expectedExceptionType != null
? expectedExceptionType.FullName
: null;
}
}
public string ExpectedExceptionName
{
get { return expectedExceptionName; }
set
{
expectedExceptionType = null;
expectedExceptionName = value;
}
}
public string ExpectedMessage
{
get { return expectedMessage; }
set { expectedMessage = value; }
}
public string MatchType
{
get { return matchType; }
set { matchType = value; }
}
public string UserMessage
{
get { return userMessage; }
set { userMessage = value; }
}
#endregion
#region Run Methods
public override void Run(TestCaseResult testResult)
{
try
{
if ( this.Parent != null)
Fixture = this.Parent.Fixture;
if (!testResult.IsFailure)
{
// Temporary... to allow for tests that directly execute a test case
if (Fixture == null)
Fixture = Reflect.Construct(this.FixtureType);
if (this.Properties["_SETCULTURE"] != null)
TestContext.CurrentCulture =
new System.Globalization.CultureInfo((string)Properties["_SETCULTURE"]);
doRun(testResult);
}
}
catch (Exception ex)
{
if (ex is NUnitException)
ex = ex.InnerException;
RecordException(ex, testResult);
}
finally
{
Fixture = null;
}
}
/// <summary>
/// The doRun method is used to run a test internally.
/// It assumes that the caller is taking care of any
/// TestFixtureSetUp and TestFixtureTearDown needed.
/// </summary>
/// <param name="testResult">The result in which to record success or failure</param>
public virtual void doRun( TestCaseResult testResult )
{
DateTime start = DateTime.Now;
try
{
if ( setUpMethod != null )
Reflect.InvokeMethod( setUpMethod, this.Fixture );
doTestCase( testResult );
}
catch(Exception ex)
{
if ( ex is NUnitException )
ex = ex.InnerException;
RecordException( ex, testResult );
}
finally
{
doTearDown( testResult );
DateTime stop = DateTime.Now;
TimeSpan span = stop.Subtract(start);
testResult.Time = (double)span.Ticks / (double)TimeSpan.TicksPerSecond;
}
}
#endregion
#region Invoke Methods by Reflection, Recording Errors
private void doTearDown( TestCaseResult testResult )
{
try
{
if ( tearDownMethod != null )
tearDownMethod.Invoke( this.Fixture, new object[0] );
}
catch(Exception ex)
{
if ( ex is NUnitException )
ex = ex.InnerException;
// TODO: What about ignore exceptions in teardown?
testResult.Error( ex,FailureSite.TearDown );
}
}
private void doTestCase( TestCaseResult testResult )
{
try
{
RunTestMethod(testResult);
ProcessNoException(testResult);
}
catch( Exception ex )
{
if ( ex is NUnitException )
ex = ex.InnerException;
if ( IsIgnoreException( ex ) )
testResult.Ignore( ex );
else
ProcessException(ex, testResult);
}
}
public virtual void RunTestMethod(TestCaseResult testResult)
{
Reflect.InvokeMethod( this.method, this.Fixture );
}
#endregion
#region Record Info About An Exception
protected void RecordException( Exception ex, TestResult testResult )
{
if ( IsIgnoreException( ex ) )
testResult.Ignore( ex.Message );
else if ( IsAssertException( ex ) )
testResult.Failure( ex.Message, ex.StackTrace );
else
testResult.Error( ex );
}
protected string GetStackTrace(Exception exception)
{
try
{
return exception.StackTrace;
}
catch( Exception )
{
return "No stack trace available";
}
}
#endregion
#region Exception Processing
protected internal virtual void ProcessNoException(TestCaseResult testResult)
{
if ( ExceptionExpected )
testResult.Failure(NoExceptionMessage(), null);
else
testResult.Success();
}
protected internal virtual void ProcessException(Exception exception, TestCaseResult testResult)
{
if (!ExceptionExpected)
{
RecordException(exception, testResult);
return;
}
if (IsExpectedExceptionType(exception))
{
if (IsExpectedMessageMatch(exception))
{
if ( exceptionHandler != null )
Reflect.InvokeMethod( exceptionHandler, this.Fixture, exception );
testResult.Success();
}
else
{
testResult.Failure(WrongTextMessage(exception), GetStackTrace(exception));
}
}
else if (IsAssertException(exception))
{
testResult.Failure(exception.Message, exception.StackTrace);
}
else
{
testResult.Failure(WrongTypeMessage(exception), GetStackTrace(exception));
}
}
#endregion
#region Abstract Methods
protected abstract bool IsAssertException(Exception ex);
protected abstract bool IsIgnoreException(Exception ex);
#endregion
#region Helper Methods
protected bool IsExpectedExceptionType(Exception exception)
{
return expectedExceptionName == null || expectedExceptionName.Equals(exception.GetType().FullName);
}
protected bool IsExpectedMessageMatch(Exception exception)
{
if (expectedMessage == null)
return true;
switch (matchType)
{
case "Exact":
default:
return expectedMessage.Equals(exception.Message);
case "Contains":
return exception.Message.IndexOf(expectedMessage) >= 0;
case "Regex":
return Regex.IsMatch(exception.Message, expectedMessage);
}
}
protected string NoExceptionMessage()
{
string expectedType = expectedExceptionName == null ? "An Exception" : expectedExceptionName;
return CombineWithUserMessage( expectedType + " was expected" );
}
protected string WrongTypeMessage(Exception exception)
{
return CombineWithUserMessage(
"An unexpected exception type was thrown" + Environment.NewLine +
"Expected: " + expectedExceptionName + Environment.NewLine +
" but was: " + exception.GetType().FullName + " : " + exception.Message );
}
protected string WrongTextMessage(Exception exception)
{
string expectedText;
switch (matchType)
{
default:
case "Exact":
expectedText = "Expected: ";
break;
case "Contains":
expectedText = "Expected message containing: ";
break;
case "Regex":
expectedText = "Expected message matching: ";
break;
}
return CombineWithUserMessage(
"The exception message text was incorrect" + Environment.NewLine +
expectedText + expectedMessage + Environment.NewLine +
" but was: " + exception.Message );
}
private string CombineWithUserMessage( string message )
{
if ( userMessage == null )
return message;
return userMessage + Environment.NewLine + message;
}
#endregion
}
}
|