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 1218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1229 new _ResultSetMatcher(Action.THROW, wrapMatcher(value), _Frequency.NONE); | 1229 new _ResultSetMatcher(Action.THROW, wrapMatcher(value), _Frequency.NONE); |
1230 | 1230 |
1231 /** The shared log used for named mocks. */ | 1231 /** The shared log used for named mocks. */ |
1232 LogEntryList sharedLog = null; | 1232 LogEntryList sharedLog = null; |
1233 | 1233 |
1234 /** The base class for all mocked objects. */ | 1234 /** The base class for all mocked objects. */ |
1235 class Mock { | 1235 class Mock { |
1236 /** The mock name. Needed if the log is shared; optional otherwise. */ | 1236 /** The mock name. Needed if the log is shared; optional otherwise. */ |
1237 final String name; | 1237 final String name; |
1238 | 1238 |
1239 /** The set of [Behavior]s supported. */ | 1239 /** |
1240 Map<String,Behavior> _behaviors; | 1240 * The set of [Behavior]s supported. Behaviors are ordered, but are |
1241 * also keyed, so we keep a hash table of indices pointing to a List. | |
1242 */ | |
1243 Map<String,int> _behaviorIndices; | |
1244 List<Behavior> _behaviors; | |
1241 | 1245 |
1242 /** The [log] of calls made. Only used if [name] is null. */ | 1246 /** The [log] of calls made. Only used if [name] is null. */ |
1243 LogEntryList log; | 1247 LogEntryList log; |
1244 | 1248 |
1245 /** How to handle unknown method calls - swallow or throw. */ | 1249 /** How to handle unknown method calls - swallow or throw. */ |
1246 final bool _throwIfNoBehavior; | 1250 final bool _throwIfNoBehavior; |
1247 | 1251 |
1248 /** Whether to create an audit log or not. */ | 1252 /** Whether to create an audit log or not. */ |
1249 bool _logging; | 1253 bool _logging; |
1250 | 1254 |
1251 bool get logging => _logging; | 1255 bool get logging => _logging; |
1252 set logging(bool value) { | 1256 set logging(bool value) { |
1253 if (value && log == null) { | 1257 if (value && log == null) { |
1254 log = new LogEntryList(); | 1258 log = new LogEntryList(); |
1255 } | 1259 } |
1256 _logging = value; | 1260 _logging = value; |
1257 } | 1261 } |
1258 | 1262 |
1259 /** | 1263 /** |
1260 * Default constructor. Unknown method calls are allowed and logged, | 1264 * Default constructor. Unknown method calls are allowed and logged, |
1261 * the mock has no name, and has its own log. | 1265 * the mock has no name, and has its own log. |
1262 */ | 1266 */ |
1263 Mock() : _throwIfNoBehavior = false, log = null, name = null { | 1267 Mock() : _throwIfNoBehavior = false, log = null, name = null { |
1264 logging = true; | 1268 logging = true; |
1265 _behaviors = new Map<String,Behavior>(); | 1269 _behaviorIndices = new Map<String,int>(); |
Siggi Cherem (dart-lang)
2013/04/30 03:25:31
what about using LinkedHashMap instead of Map?
Th
gram
2013/04/30 16:53:39
Done.
| |
1270 _behaviors = new List<Behavior>(); | |
1266 } | 1271 } |
1267 | 1272 |
1268 /** | 1273 /** |
1269 * This constructor makes a mock that has a [name] and possibly uses | 1274 * This constructor makes a mock that has a [name] and possibly uses |
1270 * a shared [log]. If [throwIfNoBehavior] is true, any calls to methods | 1275 * a shared [log]. If [throwIfNoBehavior] is true, any calls to methods |
1271 * that have no defined behaviors will throw an exception; otherwise they | 1276 * that have no defined behaviors will throw an exception; otherwise they |
1272 * will be allowed and logged (but will not do anything). | 1277 * will be allowed and logged (but will not do anything). |
1273 * If [enableLogging] is false, no logging will be done initially (whether | 1278 * If [enableLogging] is false, no logging will be done initially (whether |
1274 * or not a [log] is supplied), but [logging] can be set to true later. | 1279 * or not a [log] is supplied), but [logging] can be set to true later. |
1275 */ | 1280 */ |
1276 Mock.custom({this.name, | 1281 Mock.custom({this.name, |
1277 this.log, | 1282 this.log, |
1278 throwIfNoBehavior: false, | 1283 throwIfNoBehavior: false, |
1279 enableLogging: true}) : _throwIfNoBehavior = throwIfNoBehavior { | 1284 enableLogging: true}) : _throwIfNoBehavior = throwIfNoBehavior { |
1280 if (log != null && name == null) { | 1285 if (log != null && name == null) { |
1281 throw new Exception("Mocks with shared logs must have a name."); | 1286 throw new Exception("Mocks with shared logs must have a name."); |
1282 } | 1287 } |
1283 logging = enableLogging; | 1288 logging = enableLogging; |
1284 _behaviors = new Map<String,Behavior>(); | 1289 _behaviorIndices = new Map<String,int>(); |
1290 _behaviors = new List<Behavior>(); | |
1285 } | 1291 } |
1286 | 1292 |
1287 /** | 1293 /** |
1288 * [when] is used to create a new or extend an existing [Behavior]. | 1294 * [when] is used to create a new or extend an existing [Behavior]. |
1289 * A [CallMatcher] [filter] must be supplied, and the [Behavior]s for | 1295 * A [CallMatcher] [filter] must be supplied, and the [Behavior]s for |
1290 * that signature are returned (being created first if needed). | 1296 * that signature are returned (being created first if needed). |
1291 * | 1297 * |
1292 * Typical use case: | 1298 * Typical use case: |
1293 * | 1299 * |
1294 * mock.when(callsTo(...)).alwaysReturn(...); | 1300 * mock.when(callsTo(...)).alwaysReturn(...); |
1295 */ | 1301 */ |
1296 Behavior when(CallMatcher logFilter) { | 1302 Behavior when(CallMatcher logFilter) { |
1297 String key = logFilter.toString(); | 1303 String key = logFilter.toString(); |
1298 if (!_behaviors.containsKey(key)) { | 1304 if (!_behaviorIndices.containsKey(key)) { |
1299 Behavior b = new Behavior(logFilter); | 1305 Behavior b = new Behavior(logFilter); |
1300 _behaviors[key] = b; | 1306 _behaviorIndices[key] = _behaviors.length; |
1307 _behaviors.add(b); | |
1301 return b; | 1308 return b; |
1302 } else { | 1309 } else { |
1303 return _behaviors[key]; | 1310 return _behaviors[_behaviorIndices[key]]; |
1304 } | 1311 } |
1305 } | 1312 } |
1306 | 1313 |
1307 /** | 1314 /** |
1308 * This is the handler for method calls. We loop through the list | 1315 * This is the handler for method calls. We loop through the list |
1309 * of [Behavior]s, and find the first match that still has return | 1316 * of [Behavior]s, and find the first match that still has return |
1310 * values available, and then do the action specified by that | 1317 * values available, and then do the action specified by that |
1311 * return value. If we find no [Behavior] to apply an exception is | 1318 * return value. If we find no [Behavior] to apply an exception is |
1312 * thrown. | 1319 * thrown. |
1313 */ | 1320 */ |
1314 noSuchMethod(Invocation invocation) { | 1321 noSuchMethod(Invocation invocation) { |
1315 var method = MirrorSystem.getName(invocation.memberName); | 1322 var method = MirrorSystem.getName(invocation.memberName); |
1316 var args = invocation.positionalArguments; | 1323 var args = invocation.positionalArguments; |
1317 if (invocation.isGetter) { | 1324 if (invocation.isGetter) { |
1318 method = 'get $method'; | 1325 method = 'get $method'; |
1319 } else if (invocation.isSetter) { | 1326 } else if (invocation.isSetter) { |
1320 method = 'set $method'; | 1327 method = 'set $method'; |
1321 // Remove the trailing '='. | 1328 // Remove the trailing '='. |
1322 if (method[method.length-1] == '=') { | 1329 if (method[method.length-1] == '=') { |
1323 method = method.substring(0, method.length - 1); | 1330 method = method.substring(0, method.length - 1); |
1324 } | 1331 } |
1325 } | 1332 } |
1326 bool matchedMethodName = false; | 1333 bool matchedMethodName = false; |
1327 MatchState matchState = new MatchState(); | 1334 MatchState matchState = new MatchState(); |
1328 for (String k in _behaviors.keys) { | 1335 for (var i = 0; i < _behaviors.length; i++) { |
1329 Behavior b = _behaviors[k]; | 1336 Behavior b = _behaviors[i]; |
1330 if (b.matcher.nameFilter.matches(method, matchState)) { | 1337 if (b.matcher.nameFilter.matches(method, matchState)) { |
1331 matchedMethodName = true; | 1338 matchedMethodName = true; |
1332 } | 1339 } |
1333 if (b.matches(method, args)) { | 1340 if (b.matches(method, args)) { |
1334 List actions = b.actions; | 1341 List actions = b.actions; |
1335 if (actions == null || actions.length == 0) { | 1342 if (actions == null || actions.length == 0) { |
1336 continue; // No return values left in this Behavior. | 1343 continue; // No return values left in this Behavior. |
1337 } | 1344 } |
1338 // Get the first response. | 1345 // Get the first response. |
1339 Responder response = actions[0]; | 1346 Responder response = actions[0]; |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1479 arg4 = _noArg, | 1486 arg4 = _noArg, |
1480 arg5 = _noArg, | 1487 arg5 = _noArg, |
1481 arg6 = _noArg, | 1488 arg6 = _noArg, |
1482 arg7 = _noArg, | 1489 arg7 = _noArg, |
1483 arg8 = _noArg, | 1490 arg8 = _noArg, |
1484 arg9 = _noArg]) => | 1491 arg9 = _noArg]) => |
1485 getLogs(callsTo(method, arg0, arg1, arg2, arg3, arg4, | 1492 getLogs(callsTo(method, arg0, arg1, arg2, arg3, arg4, |
1486 arg5, arg6, arg7, arg8, arg9)); | 1493 arg5, arg6, arg7, arg8, arg9)); |
1487 | 1494 |
1488 /** Clear the behaviors for the Mock. */ | 1495 /** Clear the behaviors for the Mock. */ |
1489 void resetBehavior() => _behaviors.clear(); | 1496 void resetBehavior() { |
1497 _behaviorIndices.clear(); | |
1498 _behaviors.clear(); | |
1499 } | |
1490 | 1500 |
1491 /** Clear the logs for the Mock. */ | 1501 /** Clear the logs for the Mock. */ |
1492 void clearLogs() { | 1502 void clearLogs() { |
1493 if (log != null) { | 1503 if (log != null) { |
1494 if (name == null) { // This log is not shared. | 1504 if (name == null) { // This log is not shared. |
1495 log.logs.clear(); | 1505 log.logs.clear(); |
1496 } else { // This log may be shared. | 1506 } else { // This log may be shared. |
1497 log.logs = log.logs.where((e) => e.mockName != name).toList(); | 1507 log.logs = log.logs.where((e) => e.mockName != name).toList(); |
1498 } | 1508 } |
1499 } | 1509 } |
1500 } | 1510 } |
1501 | 1511 |
1502 /** Clear both logs and behavior. */ | 1512 /** Clear both logs and behavior. */ |
1503 void reset() { | 1513 void reset() { |
1504 resetBehavior(); | 1514 resetBehavior(); |
1505 clearLogs(); | 1515 clearLogs(); |
1506 } | 1516 } |
1507 } | 1517 } |
OLD | NEW |