Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(385)

Side by Side Diff: pkg/unittest/lib/mock.dart

Issue 14734004: Spys with mirrors! (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | pkg/unittest/test/mock_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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 rtn = mirror.delegate(invocation);
Siggi Cherem (dart-lang) 2013/05/02 01:01:26 nit: rtn => result, res, or returnedValue?
1360 log.add(new LogEntry(name, method, args, Action.PROXY, rtn));
1361 return rtn;
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | pkg/unittest/test/mock_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698