| 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 * A simple mocking/spy library. | 6 * A simple mocking/spy library. |
| 7 * | 7 * |
| 8 * ## Installing ## | 8 * ## Installing ## |
| 9 * | 9 * |
| 10 * Use [pub][] to install this package. Add the following to your `pubspec.yaml` | 10 * Use [pub][] to install this package. Add the following to your `pubspec.yaml` |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 95 * } | 95 * } |
| 96 * | 96 * |
| 97 * class MockFoo extends Mock implements Foo { | 97 * class MockFoo extends Mock implements Foo { |
| 98 * Foo real; | 98 * Foo real; |
| 99 * MockFoo() { | 99 * MockFoo() { |
| 100 * real = new Foo(); | 100 * real = new Foo(); |
| 101 * this.when(callsTo('bar')).alwaysCall(real.bar); | 101 * this.when(callsTo('bar')).alwaysCall(real.bar); |
| 102 * } | 102 * } |
| 103 * } | 103 * } |
| 104 * | 104 * |
| 105 * However, there is an even easier way, by calling [Mock.spy], e.g.: |
| 106 * |
| 107 * var foo = new Foo(); |
| 108 * var spy = new Mock.spy(foo); |
| 109 * print(spy.bar(1, 2, 3)); |
| 110 * |
| 111 * Spys created with Mock.spy do not have user-defined behavior; |
| 112 * they are simply proxies, and thus will throw an exception if |
| 113 * you call [when]. They capture all calls in the log, so you can |
| 114 * do assertions on their history, such as: |
| 115 * |
| 116 * spy.getLogs(callsTo('bar')).verify(happenedOnce); |
| 117 * |
| 105 * [pub]: http://pub.dartlang.org | 118 * [pub]: http://pub.dartlang.org |
| 106 */ | 119 */ |
| 107 | 120 |
| 108 library mock; | 121 library mock; |
| 109 | 122 |
| 110 import 'dart:mirrors' show MirrorSystem; | 123 import 'dart:mirrors'; |
| 111 import 'dart:collection' show LinkedHashMap; | 124 import 'dart:collection' show LinkedHashMap; |
| 112 | 125 |
| 113 import 'matcher.dart'; | 126 import 'matcher.dart'; |
| 114 | 127 |
| 115 /** | 128 /** |
| 116 * The error formatter for mocking is a bit different from the default one | 129 * The error formatter for mocking is a bit different from the default one |
| 117 * for unit testing; instead of the third argument being a 'reason' | 130 * for unit testing; instead of the third argument being a 'reason' |
| 118 * it is instead a [signature] describing the method signature filter | 131 * it is instead a [signature] describing the method signature filter |
| 119 * that was used to select the logs that were verified. | 132 * that was used to select the logs that were verified. |
| 120 */ | 133 */ |
| (...skipping 1118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1239 | 1252 |
| 1240 /** The set of [Behavior]s supported. */ | 1253 /** The set of [Behavior]s supported. */ |
| 1241 LinkedHashMap<String,Behavior> _behaviors; | 1254 LinkedHashMap<String,Behavior> _behaviors; |
| 1242 | 1255 |
| 1243 /** The [log] of calls made. Only used if [name] is null. */ | 1256 /** The [log] of calls made. Only used if [name] is null. */ |
| 1244 LogEntryList log; | 1257 LogEntryList log; |
| 1245 | 1258 |
| 1246 /** How to handle unknown method calls - swallow or throw. */ | 1259 /** How to handle unknown method calls - swallow or throw. */ |
| 1247 final bool _throwIfNoBehavior; | 1260 final bool _throwIfNoBehavior; |
| 1248 | 1261 |
| 1262 /** For spys, the real object that we are spying on. */ |
| 1263 Object _realObject; |
| 1264 |
| 1249 /** Whether to create an audit log or not. */ | 1265 /** Whether to create an audit log or not. */ |
| 1250 bool _logging; | 1266 bool _logging; |
| 1251 | 1267 |
| 1252 bool get logging => _logging; | 1268 bool get logging => _logging; |
| 1253 set logging(bool value) { | 1269 set logging(bool value) { |
| 1254 if (value && log == null) { | 1270 if (value && log == null) { |
| 1255 log = new LogEntryList(); | 1271 log = new LogEntryList(); |
| 1256 } | 1272 } |
| 1257 _logging = value; | 1273 _logging = value; |
| 1258 } | 1274 } |
| (...skipping 20 matching lines...) Expand all Loading... |
| 1279 throwIfNoBehavior: false, | 1295 throwIfNoBehavior: false, |
| 1280 enableLogging: true}) : _throwIfNoBehavior = throwIfNoBehavior { | 1296 enableLogging: true}) : _throwIfNoBehavior = throwIfNoBehavior { |
| 1281 if (log != null && name == null) { | 1297 if (log != null && name == null) { |
| 1282 throw new Exception("Mocks with shared logs must have a name."); | 1298 throw new Exception("Mocks with shared logs must have a name."); |
| 1283 } | 1299 } |
| 1284 logging = enableLogging; | 1300 logging = enableLogging; |
| 1285 _behaviors = new LinkedHashMap<String,Behavior>(); | 1301 _behaviors = new LinkedHashMap<String,Behavior>(); |
| 1286 } | 1302 } |
| 1287 | 1303 |
| 1288 /** | 1304 /** |
| 1305 * This constructor creates a spy with no user-defined behavior. |
| 1306 * This is simply a proxy for a real object that passes calls |
| 1307 * through to that real object but captures an audit trail of |
| 1308 * calls made to the object that can be queried and validated |
| 1309 * later. |
| 1310 */ |
| 1311 Mock.spy(this._realObject, {this.name, this.log}) |
| 1312 : _behaviors = null, |
| 1313 _throwIfNoBehavior = true { |
| 1314 logging = true; |
| 1315 } |
| 1316 |
| 1317 /** |
| 1289 * [when] is used to create a new or extend an existing [Behavior]. | 1318 * [when] is used to create a new or extend an existing [Behavior]. |
| 1290 * A [CallMatcher] [filter] must be supplied, and the [Behavior]s for | 1319 * A [CallMatcher] [filter] must be supplied, and the [Behavior]s for |
| 1291 * that signature are returned (being created first if needed). | 1320 * that signature are returned (being created first if needed). |
| 1292 * | 1321 * |
| 1293 * Typical use case: | 1322 * Typical use case: |
| 1294 * | 1323 * |
| 1295 * mock.when(callsTo(...)).alwaysReturn(...); | 1324 * mock.when(callsTo(...)).alwaysReturn(...); |
| 1296 */ | 1325 */ |
| 1297 Behavior when(CallMatcher logFilter) { | 1326 Behavior when(CallMatcher logFilter) { |
| 1298 String key = logFilter.toString(); | 1327 String key = logFilter.toString(); |
| (...skipping 18 matching lines...) Expand all Loading... |
| 1317 var args = invocation.positionalArguments; | 1346 var args = invocation.positionalArguments; |
| 1318 if (invocation.isGetter) { | 1347 if (invocation.isGetter) { |
| 1319 method = 'get $method'; | 1348 method = 'get $method'; |
| 1320 } else if (invocation.isSetter) { | 1349 } else if (invocation.isSetter) { |
| 1321 method = 'set $method'; | 1350 method = 'set $method'; |
| 1322 // Remove the trailing '='. | 1351 // Remove the trailing '='. |
| 1323 if (method[method.length-1] == '=') { | 1352 if (method[method.length-1] == '=') { |
| 1324 method = method.substring(0, method.length - 1); | 1353 method = method.substring(0, method.length - 1); |
| 1325 } | 1354 } |
| 1326 } | 1355 } |
| 1356 if (_behaviors == null) { // Spy. |
| 1357 var mirror = reflect(_realObject); |
| 1358 try { |
| 1359 var result = mirror.delegate(invocation); |
| 1360 log.add(new LogEntry(name, method, args, Action.PROXY, result)); |
| 1361 return result; |
| 1362 } catch (e) { |
| 1363 log.add(new LogEntry(name, method, args, Action.THROW, e)); |
| 1364 throw e; |
| 1365 } |
| 1366 } |
| 1327 bool matchedMethodName = false; | 1367 bool matchedMethodName = false; |
| 1328 MatchState matchState = new MatchState(); | 1368 MatchState matchState = new MatchState(); |
| 1329 for (String k in _behaviors.keys) { | 1369 for (String k in _behaviors.keys) { |
| 1330 Behavior b = _behaviors[k]; | 1370 Behavior b = _behaviors[k]; |
| 1331 if (b.matcher.nameFilter.matches(method, matchState)) { | 1371 if (b.matcher.nameFilter.matches(method, matchState)) { |
| 1332 matchedMethodName = true; | 1372 matchedMethodName = true; |
| 1333 } | 1373 } |
| 1334 if (b.matches(method, args)) { | 1374 if (b.matches(method, args)) { |
| 1335 List actions = b.actions; | 1375 List actions = b.actions; |
| 1336 if (actions == null || actions.length == 0) { | 1376 if (actions == null || actions.length == 0) { |
| (...skipping 16 matching lines...) Expand all Loading... |
| 1353 log.add(new LogEntry(name, method, args, action, value)); | 1393 log.add(new LogEntry(name, method, args, action, value)); |
| 1354 } | 1394 } |
| 1355 return value; | 1395 return value; |
| 1356 } else if (action == Action.THROW) { | 1396 } else if (action == Action.THROW) { |
| 1357 if (_logging && b.logging) { | 1397 if (_logging && b.logging) { |
| 1358 log.add(new LogEntry(name, method, args, action, value)); | 1398 log.add(new LogEntry(name, method, args, action, value)); |
| 1359 } | 1399 } |
| 1360 throw value; | 1400 throw value; |
| 1361 } else if (action == Action.PROXY) { | 1401 } else if (action == Action.PROXY) { |
| 1362 // TODO(gram): Replace all this with: | 1402 // TODO(gram): Replace all this with: |
| 1363 // var rtn = invocation.invokeOn(value); | 1403 // var rtn = reflect(value).apply(invocation.positionalArguments, |
| 1404 // invocation.namedArguments); |
| 1364 // once that is supported. | 1405 // once that is supported. |
| 1365 var rtn; | 1406 var rtn; |
| 1366 switch (args.length) { | 1407 switch (args.length) { |
| 1367 case 0: | 1408 case 0: |
| 1368 rtn = value(); | 1409 rtn = value(); |
| 1369 break; | 1410 break; |
| 1370 case 1: | 1411 case 1: |
| 1371 rtn = value(args[0]); | 1412 rtn = value(args[0]); |
| 1372 break; | 1413 break; |
| 1373 case 2: | 1414 case 2: |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1499 } | 1540 } |
| 1500 } | 1541 } |
| 1501 } | 1542 } |
| 1502 | 1543 |
| 1503 /** Clear both logs and behavior. */ | 1544 /** Clear both logs and behavior. */ |
| 1504 void reset() { | 1545 void reset() { |
| 1505 resetBehavior(); | 1546 resetBehavior(); |
| 1506 clearLogs(); | 1547 clearLogs(); |
| 1507 } | 1548 } |
| 1508 } | 1549 } |
| OLD | NEW |