Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * The error formatter for mocking is a bit different from the default one | 6 * The error formatter for mocking is a bit different from the default one |
| 7 * for unit testing; instead of the third argument being a 'reason' | 7 * for unit testing; instead of the third argument being a 'reason' |
| 8 * it is instead a [signature] describing the method signature filter | 8 * it is instead a [signature] describing the method signature filter |
| 9 * that was used to select the logs that were verified. | 9 * that was used to select the logs that were verified. |
| 10 */ | 10 */ |
| (...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 197 arg5, arg6, arg7, arg8, arg9); | 197 arg5, arg6, arg7, arg8, arg9); |
| 198 } | 198 } |
| 199 | 199 |
| 200 /** | 200 /** |
| 201 * A [Behavior] represents how a [Mock] will respond to one particular | 201 * A [Behavior] represents how a [Mock] will respond to one particular |
| 202 * type of method call. | 202 * type of method call. |
| 203 */ | 203 */ |
| 204 class Behavior { | 204 class Behavior { |
| 205 CallMatcher matcher; // The method call matcher. | 205 CallMatcher matcher; // The method call matcher. |
| 206 List<Responder> actions; // The values to return/throw or proxies to call. | 206 List<Responder> actions; // The values to return/throw or proxies to call. |
| 207 bool logging = true; | |
| 207 | 208 |
| 208 Behavior (this.matcher) { | 209 Behavior (this.matcher) { |
| 209 actions = new List<Responder>(); | 210 actions = new List<Responder>(); |
| 210 } | 211 } |
| 211 | 212 |
| 212 /** | 213 /** |
| 213 * Adds a [Responder] that returns a [value] for [count] calls | 214 * Adds a [Responder] that returns a [value] for [count] calls |
| 214 * (1 by default). | 215 * (1 by default). |
| 215 */ | 216 */ |
| 216 Behavior thenReturn(value, [count = 1]) { | 217 Behavior thenReturn(value, [count = 1]) { |
| (...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 420 LogEntryList verify(Matcher matcher) { | 421 LogEntryList verify(Matcher matcher) { |
| 421 if (_mockFailureHandler == null) { | 422 if (_mockFailureHandler == null) { |
| 422 _mockFailureHandler = | 423 _mockFailureHandler = |
| 423 new _MockFailureHandler(getOrCreateExpectFailureHandler()); | 424 new _MockFailureHandler(getOrCreateExpectFailureHandler()); |
| 424 } | 425 } |
| 425 expect(logs, matcher, filter, _mockFailureHandler); | 426 expect(logs, matcher, filter, _mockFailureHandler); |
| 426 return this; | 427 return this; |
| 427 } | 428 } |
| 428 | 429 |
| 429 /** | 430 /** |
| 431 * Iterate through the list and call the [validator] function with the | |
| 432 * log [List] and position. The [validator] should return the number of | |
| 433 * positions to advance upon success, or zero upon failure. When zero is | |
| 434 * returned an error is reported. [reason] can be used to provide a | |
| 435 * more descriptive failure message. If a failure occurred false will be | |
| 436 * returned (unless the failure handler itself threw an exception); | |
| 437 * otherwise true is returned. | |
| 438 * The use case here is to perform more complex validations; for example | |
| 439 * we may want to assert that the return value from some function is | |
| 440 * later used as a parameter to a following function. If we filter the logs | |
| 441 * to include just these two functions we can write a simple validator to | |
| 442 * do this check. | |
| 443 */ | |
| 444 bool stepwiseValidate(Function validator, [String reason = '']) { | |
|
Siggi Cherem (dart-lang)
2012/08/07 23:58:47
looking at the test, I think it is worth defining
| |
| 445 if (_mockFailureHandler == null) { | |
| 446 _mockFailureHandler = | |
| 447 new _MockFailureHandler(getOrCreateExpectFailureHandler()); | |
| 448 } | |
| 449 var i = 0; | |
| 450 while (i < logs.length) { | |
| 451 var n = validator(logs, i); | |
| 452 if (n == 0) { | |
| 453 if (reason.length > 0) { | |
| 454 reason = ': $reason'; | |
| 455 } | |
| 456 _mockFailureHandler.fail("Stepwise validation failed at $filter " | |
| 457 "position $i$reason"); | |
| 458 return false; | |
| 459 } else { | |
| 460 i += n; | |
| 461 } | |
| 462 } | |
| 463 return true; | |
| 464 } | |
| 465 | |
| 466 /** | |
| 430 * Turn the logs into human-readable text. If [baseTime] is specified | 467 * Turn the logs into human-readable text. If [baseTime] is specified |
| 431 * then each entry is prefixed with the offset from that time in | 468 * then each entry is prefixed with the offset from that time in |
| 432 * milliseconds; otherwise the time of day is used. | 469 * milliseconds; otherwise the time of day is used. |
| 433 */ | 470 */ |
| 434 String toString([Date baseTime]) { | 471 String toString([Date baseTime]) { |
| 435 String s = ''; | 472 String s = ''; |
| 436 for (var e in logs) { | 473 for (var e in logs) { |
| 437 s = '$s${e.toString(baseTime)}\n'; | 474 s = '$s${e.toString(baseTime)}\n'; |
| 438 } | 475 } |
| 439 return s; | 476 return s; |
| 440 } | 477 } |
| 441 | 478 |
| 442 /** | 479 /** |
| 443 * Find the first log entry that satisfies [logFilter] and | 480 * Find the first log entry that satisfies [logFilter] and |
| 444 * return its position. A search [start] position can be provided | 481 * return its position. A search [start] position can be provided |
| 445 * to allow for repeated searches. [logFilter] can be a [CallMatcher], | 482 * to allow for repeated searches. [logFilter] can be a [CallMatcher], |
| 446 * or a predicate function that takes a [LogEntry] argument and returns | 483 * or a predicate function that takes a [LogEntry] argument and returns |
| 447 * a bool. If [logFilter] is null, it will match any [LogEntry]. | 484 * a bool. If [logFilter] is null, it will match any [LogEntry]. |
| 448 * If no entry is found, then [failureReturnValue] is returned. | 485 * If no entry is found, then [failureReturnValue] is returned. |
| 486 * After each check the position is updated by [skip], so using | |
| 487 * [skip] of -1 allows backward searches. | |
| 449 */ | 488 */ |
| 450 int findLogEntry(logFilter, [int start = 0, int failureReturnValue = -1]) { | 489 int findLogEntry(logFilter, [int start = 0, int failureReturnValue = -1, |
| 490 skip = 1]) { | |
| 451 logFilter = _makePredicate(logFilter); | 491 logFilter = _makePredicate(logFilter); |
| 452 int pos = start; | 492 int pos = start; |
| 453 while (pos < logs.length) { | 493 while (pos >= 0 && pos < logs.length) { |
| 454 if (logFilter(logs[pos])) { | 494 if (logFilter(logs[pos])) { |
| 455 return pos; | 495 return pos; |
| 456 } | 496 } |
| 457 ++pos; | 497 pos += skip; |
| 458 } | 498 } |
| 459 return failureReturnValue; | 499 return failureReturnValue; |
| 460 } | 500 } |
| 461 | 501 |
| 462 /** | 502 /** |
| 463 * Returns log events that happened up to the first one that | 503 * Returns log events that happened up to the first one that |
| 464 * satisfies [logFilter]. If [inPlace] is true, then returns | 504 * satisfies [logFilter]. If [inPlace] is true, then returns |
| 465 * this LogEntryList after removing the from the first satisfier; | 505 * this LogEntryList after removing the from the first satisfier; |
| 466 * onwards otherwise a new list is created. [description] | 506 * onwards otherwise a new list is created. [description] |
| 467 * is used to create a new name for the resulting list. | 507 * is used to create a new name for the resulting list. |
| (...skipping 595 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1063 LogEntryList sharedLog = null; | 1103 LogEntryList sharedLog = null; |
| 1064 | 1104 |
| 1065 /** | 1105 /** |
| 1066 * [Mock] is the base class for all mocked objects, with | 1106 * [Mock] is the base class for all mocked objects, with |
| 1067 * support for basic mocking. | 1107 * support for basic mocking. |
| 1068 * | 1108 * |
| 1069 * To create a mock objects for some class T, create a new class using: | 1109 * To create a mock objects for some class T, create a new class using: |
| 1070 * | 1110 * |
| 1071 * class MockT extends Mock implements T {}; | 1111 * class MockT extends Mock implements T {}; |
| 1072 * | 1112 * |
| 1073 * Then specify the behavior of the Mock for different methods using | 1113 * Then specify the [Behavior] of the Mock for different methods using |
| 1074 * [when] (to select the method and parameters) and [thenReturn], | 1114 * [when] (to select the method and parameters) and then the [Action]s |
| 1075 * [alwaysReturn], [thenThrow], [alwaysThrow], [thenCall] or [alwaysCall]. | 1115 * for the [Behavior] by calling [thenReturn], [alwaysReturn], [thenThrow], |
| 1116 * [alwaysThrow], [thenCall] or [alwaysCall]. | |
| 1117 * | |
| 1076 * [thenReturn], [thenThrow] and [thenCall] are one-shot so you would | 1118 * [thenReturn], [thenThrow] and [thenCall] are one-shot so you would |
| 1077 * typically call these more than once to specify a sequence of actions; | 1119 * typically call these more than once to specify a sequence of actions; |
| 1078 * this can be done with chained calls, e.g.: | 1120 * this can be done with chained calls, e.g.: |
| 1079 * | 1121 * |
| 1080 * m.when(callsTo('foo')). | 1122 * m.when(callsTo('foo')). |
| 1081 * thenReturn(0).thenReturn(1).thenReturn(2); | 1123 * thenReturn(0).thenReturn(1).thenReturn(2); |
| 1082 * | 1124 * |
| 1083 * [thenCall] and [alwaysCall] allow you to proxy mocked methods, chaining | 1125 * [thenCall] and [alwaysCall] allow you to proxy mocked methods, chaining |
| 1084 * to some other implementation. This provides a way to implement 'spies'. | 1126 * to some other implementation. This provides a way to implement 'spies'. |
| 1085 * | 1127 * |
| 1128 * You can disable logging for a particular [Behavior] easily: | |
| 1129 * | |
| 1130 * m.when(callsTo('bar')).logging = false; | |
| 1131 * | |
| 1086 * You can then use the mock object. Once you are done, to verify the | 1132 * You can then use the mock object. Once you are done, to verify the |
| 1087 * behavior, use [getLogs] to extract a relevant subset of method call | 1133 * behavior, use [getLogs] to extract a relevant subset of method call |
| 1088 * logs and apply [Matchers] to these through calling [verify]. | 1134 * logs and apply [Matchers] to these through calling [verify]. |
| 1089 * | 1135 * |
| 1090 * A Mock can be given a name when constructed. In this case instead of | 1136 * A Mock can be given a name when constructed. In this case instead of |
| 1091 * keeping its own log, it uses a shared log. This can be useful to get an | 1137 * keeping its own log, it uses a shared log. This can be useful to get an |
| 1092 * audit trail of interleaved behavior. It is the responsibility of the user | 1138 * audit trail of interleaved behavior. It is the responsibility of the user |
| 1093 * to ensure that mock names, if used, are unique. | 1139 * to ensure that mock names, if used, are unique. |
| 1094 * | 1140 * |
| 1095 * Limitations: | 1141 * Limitations: |
| (...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1240 // Note that for endlessly repeating values, we started the count at | 1286 // Note that for endlessly repeating values, we started the count at |
| 1241 // 0, so we get a potentially useful value here, which is the | 1287 // 0, so we get a potentially useful value here, which is the |
| 1242 // (negation of) the number of times we returned the value. | 1288 // (negation of) the number of times we returned the value. |
| 1243 if (--response.count == 0) { | 1289 if (--response.count == 0) { |
| 1244 actions.removeRange(0, 1); | 1290 actions.removeRange(0, 1); |
| 1245 } | 1291 } |
| 1246 // Do the response. | 1292 // Do the response. |
| 1247 Action action = response.action; | 1293 Action action = response.action; |
| 1248 var value = response.value; | 1294 var value = response.value; |
| 1249 if (action == Action.RETURN) { | 1295 if (action == Action.RETURN) { |
| 1250 if (_logging) { | 1296 if (_logging && b.logging) { |
| 1251 log.add(new LogEntry(name, method, args, action, value)); | 1297 log.add(new LogEntry(name, method, args, action, value)); |
| 1252 } | 1298 } |
| 1253 return value; | 1299 return value; |
| 1254 } else if (action == Action.THROW) { | 1300 } else if (action == Action.THROW) { |
| 1255 if (_logging) { | 1301 if (_logging && b.logging) { |
| 1256 log.add(new LogEntry(name, method, args, action, value)); | 1302 log.add(new LogEntry(name, method, args, action, value)); |
| 1257 } | 1303 } |
| 1258 throw value; | 1304 throw value; |
| 1259 } else if (action == Action.PROXY) { | 1305 } else if (action == Action.PROXY) { |
| 1260 var rtn; | 1306 var rtn; |
| 1261 switch (args.length) { | 1307 switch (args.length) { |
| 1262 case 0: | 1308 case 0: |
| 1263 rtn = value(); | 1309 rtn = value(); |
| 1264 break; | 1310 break; |
| 1265 case 1: | 1311 case 1: |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 1294 args[4], args[5], args[6], args[7], args[8]); | 1340 args[4], args[5], args[6], args[7], args[8]); |
| 1295 break; | 1341 break; |
| 1296 case 9: | 1342 case 9: |
| 1297 rtn = value(args[0], args[1], args[2], args[3], | 1343 rtn = value(args[0], args[1], args[2], args[3], |
| 1298 args[4], args[5], args[6], args[7], args[8], args[9]); | 1344 args[4], args[5], args[6], args[7], args[8], args[9]); |
| 1299 break; | 1345 break; |
| 1300 default: | 1346 default: |
| 1301 throw new Exception( | 1347 throw new Exception( |
| 1302 "Cannot proxy calls with more than 10 parameters."); | 1348 "Cannot proxy calls with more than 10 parameters."); |
| 1303 } | 1349 } |
| 1304 if (_logging) { | 1350 if (_logging && b.logging) { |
| 1305 log.add(new LogEntry(name, method, args, action, rtn)); | 1351 log.add(new LogEntry(name, method, args, action, rtn)); |
| 1306 } | 1352 } |
| 1307 return rtn; | 1353 return rtn; |
| 1308 } | 1354 } |
| 1309 } | 1355 } |
| 1310 } | 1356 } |
| 1311 if (matchedMethodName) { | 1357 if (matchedMethodName) { |
| 1312 // User did specify behavior for this method, but all the | 1358 // User did specify behavior for this method, but all the |
| 1313 // actions are exhausted. This is considered an error. | 1359 // actions are exhausted. This is considered an error. |
| 1314 throw new Exception('No more actions for method ' | 1360 throw new Exception('No more actions for method ' |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1374 arg3 = _noArg, | 1420 arg3 = _noArg, |
| 1375 arg4 = _noArg, | 1421 arg4 = _noArg, |
| 1376 arg5 = _noArg, | 1422 arg5 = _noArg, |
| 1377 arg6 = _noArg, | 1423 arg6 = _noArg, |
| 1378 arg7 = _noArg, | 1424 arg7 = _noArg, |
| 1379 arg8 = _noArg, | 1425 arg8 = _noArg, |
| 1380 arg9 = _noArg]) => | 1426 arg9 = _noArg]) => |
| 1381 getLogs(callsTo(method, arg0, arg1, arg2, arg3, arg4, | 1427 getLogs(callsTo(method, arg0, arg1, arg2, arg3, arg4, |
| 1382 arg5, arg6, arg7, arg8, arg9)); | 1428 arg5, arg6, arg7, arg8, arg9)); |
| 1383 } | 1429 } |
| OLD | NEW |