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

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

Issue 10718002: Changed the syntax some. All the handling of variable argument lists is (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 5 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
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 * 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 22 matching lines...) Expand all
33 } 33 }
34 } 34 }
35 35
36 _MockFailureHandler _mockFailureHandler = null; 36 _MockFailureHandler _mockFailureHandler = null;
37 37
38 /** 38 /**
39 * [_noArg] is a sentinel value representing no argument. 39 * [_noArg] is a sentinel value representing no argument.
40 */ 40 */
41 final _noArg = const _Sentinel(); 41 final _noArg = const _Sentinel();
42 42
43 /** The ways in which a call to a mock method can be handled. */
44 final RETURN = 0;
45 final THROW = 1;
46 final PROXY = 2;
47
43 /** 48 /**
44 * The behavior of a method call in the mock library is specified 49 * The behavior of a method call in the mock library is specified
45 * with [BehaviorValue]s. A [BehaviorValue] has a [value] to throw 50 * with [BehaviorValue]s. A [BehaviorValue] has a [value] to throw
46 * or return (depending on whether [isThrow] is true or not, respectively), 51 * or return (depending on whether [isThrow] is true or not, respectively),
47 * and can either be one-shot, multi-shot, or infinitely repeating, 52 * and can either be one-shot, multi-shot, or infinitely repeating,
48 * depending on the value of [count (1, greater than 1, or 0 respectively). 53 * depending on the value of [count (1, greater than 1, or 0 respectively).
49 */ 54 */
50 class BehaviorValue { 55 class BehaviorValue {
51 var value; 56 var value;
52 bool isThrow; 57 int action;
53 int count; 58 int count;
54 BehaviorValue(this.value, [this.count = 1, this.isThrow = false]); 59 BehaviorValue(this.value, [this.count = 1, this.action = RETURN]);
55 } 60 }
56 61
57 /** 62 /**
58 * A [CallMatcher] is a special matcher used to match method calls (i.e. 63 * A [CallMatcher] is a special matcher used to match method calls (i.e.
59 * a method name and set of arguments). It is not a [Matcher] like the 64 * a method name and set of arguments). It is not a [Matcher] like the
60 * unit test [Matcher], but instead represents a collection of [Matcher]s, 65 * unit test [Matcher], but instead represents a collection of [Matcher]s,
61 * one per argument, that will be applied to the parameters to decide if 66 * one per argument, that will be applied to the parameters to decide if
62 * the method call is a match. 67 * the method call is a match.
63 */ 68 */
64 class CallMatcher { 69 class CallMatcher {
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
115 } 120 }
116 d.add(')'); 121 d.add(')');
117 return d.toString(); 122 return d.toString();
118 } 123 }
119 124
120 /** 125 /**
121 * Given a [method] name oand list of [arguments], return true 126 * Given a [method] name oand list of [arguments], return true
122 * if it matches this [CallMatcher. 127 * if it matches this [CallMatcher.
123 */ 128 */
124 bool matches(String method, List arguments) { 129 bool matches(String method, List arguments) {
125 if (method != this.name || arguments.length != argMatchers.length) { 130 if (method != this.name) {
126 return false; 131 return false;
127 } 132 }
128 for (var i = 0; i < arguments.length; i++) { 133 if (arguments.length < argMatchers.length) {
134 throw new Exception("Less arguments than matchers for $name");
135 }
136 for (var i = 0; i < argMatchers.length; i++) {
129 if (!argMatchers[i].matches(arguments[i])) { 137 if (!argMatchers[i].matches(arguments[i])) {
130 return false; 138 return false;
131 } 139 }
132 } 140 }
133 return true; 141 return true;
134 } 142 }
135 } 143 }
136 144
145 /** [callstTo] returns a CallMatcher for the specified signature. */
146 CallMatcher callsTo(String method, [ arg0 = _noArg,
147 arg1 = _noArg,
148 arg2 = _noArg,
149 arg3 = _noArg,
150 arg4 = _noArg,
151 arg5 = _noArg,
152 arg6 = _noArg,
153 arg7 = _noArg,
154 arg8 = _noArg,
155 arg9 = _noArg]) {
156 return new CallMatcher(method, arg0, arg1, arg2, arg3, arg4,
157 arg5, arg6, arg7, arg8, arg9);
158 }
159
137 /** 160 /**
138 * A [Behavior] represents how a [Mock] will respond to one particular 161 * A [Behavior] represents how a [Mock] will respond to one particular
139 * type of method call. 162 * type of method call.
140 */ 163 */
141 class Behavior { 164 class Behavior {
142 CallMatcher matcher; // The method call matcher. 165 CallMatcher matcher; // The method call matcher.
143 List<BehaviorValue> returnValues; // The values to return/throw. 166 List<BehaviorValue> actions; // The values to return/throw or proxies to call.
144 167
145 Behavior (this.matcher) { 168 Behavior (this.matcher) {
146 returnValues = new List<BehaviorValue>(); 169 actions = new List<BehaviorValue>();
147 } 170 }
148 171
149 /** 172 /**
150 * [thenReturn] creates a return value, that is returned [count] 173 * [thenReturn] creates a return value, that is returned [count]
151 * times (1 by default). 174 * times (1 by default).
152 */ 175 */
153 Behavior thenReturn(value, [count = 1]) { 176 Behavior thenReturn(value, [count = 1]) {
154 returnValues.add(new BehaviorValue(value, count)); 177 actions.add(new BehaviorValue(value, count, RETURN));
155 return this; // For chaining calls. 178 return this; // For chaining calls.
156 } 179 }
157 180
158 /** [alwaysReturn] creates a repeating return value. */ 181 /** [alwaysReturn] creates a repeating return value. */
159 Behavior alwaysReturn(value) { 182 Behavior alwaysReturn(value) {
160 return thenReturn(value, 0); 183 return thenReturn(value, 0);
161 } 184 }
162 185
163 /** 186 /**
164 * [thenThrow] creates an exception, that is thrown [count] 187 * [thenThrow] creates an exception, that is thrown [count]
165 * times (1 by default). 188 * times (1 by default).
166 */ 189 */
167 Behavior thenThrow(value, [count = 1]) { 190 Behavior thenThrow(value, [count = 1]) {
168 returnValues.add(new BehaviorValue(value, count, true)); 191 actions.add(new BehaviorValue(value, count, THROW));
169 return this; // For chaining calls. 192 return this; // For chaining calls.
170 } 193 }
171 194
172 /** [alwaysThrow] creates a repeating exception. */ 195 /** [alwaysThrow] creates a repeating exception. */
173 Behavior alwaysThrow(value) { 196 Behavior alwaysThrow(value) {
174 return thenThrow(value, 0); 197 return thenThrow(value, 0);
175 } 198 }
176 199
200 /**
201 * [thenCall] creates a proxy, that is called [count]
202 * times (1 by default).
203 */
204 Behavior thenCall(value, [count = 1]) {
Siggi Cherem (dart-lang) 2012/06/28 00:44:40 what is [value]. Looking a the code in the test, s
gram 2012/06/28 17:32:54 Added a comment.
205 actions.add(new BehaviorValue(value, count, PROXY));
206 return this; // For chaining calls.
207 }
208
209 /** [alwaysCall] creates a repeating proxy call. */
210 Behavior alwaysCall(value) {
211 return thenCall(value, 0);
212 }
213
177 /** [matches] return true if a method call matches the [Behavior]. */ 214 /** [matches] return true if a method call matches the [Behavior]. */
178 bool matches(name, args) => matcher.matches(name, args); 215 bool matches(name, args) => matcher.matches(name, args);
179 } 216 }
180 217
181 /** 218 /**
182 * Every call to a [Mock] object method is logged. The logs are 219 * Every call to a [Mock] object method is logged. The logs are
183 * kept in instances of [LogEntry]. 220 * kept in instances of [LogEntry].
184 */ 221 */
185 class LogEntry { 222 class LogEntry {
186 final String name; // The method name. 223 final String name; // The method name.
187 final List args; // The parameters. 224 final List args; // The parameters.
188 final BehaviorValue result; // The behavior that resulted. 225 final int action; // The behavior that resulted.
226 final value; // The value that was returned (if no throw).
189 227
190 const LogEntry(this.name, this.args, this.result); 228 const LogEntry(this.name, this.args, this.action, [this.value = null]);
191 } 229 }
192 230
193 /** 231 /**
194 * We do verification on a list of [LogEntry]s. To allow chaining 232 * We do verification on a list of [LogEntry]s. To allow chaining
195 * of calls to verify, we encapsulate such a list in the [LogEntryList] 233 * of calls to verify, we encapsulate such a list in the [LogEntryList]
196 * class. 234 * class.
197 */ 235 */
198 class LogEntryList { 236 class LogEntryList {
199 final String filter; 237 final String filter;
200 final List<LogEntry> logs; 238 final List<LogEntry> logs;
201 const LogEntryList(this.logs, [this.filter = null]); 239 const LogEntryList(this.logs, [this.filter = null]);
202 240
203 /** Add a [LogEntry] to the log. */ 241 /** Add a [LogEntry] to the log. */
204 add(LogEntry entry) => logs.add(entry); 242 add(LogEntry entry) => logs.add(entry);
205 243
206 /** 244 /**
207 * Create a new [LogEntryList] consisting of [LogEntry]s from 245 * Create a new [LogEntryList] consisting of [LogEntry]s from
208 * this list that match the specified [logfilter]. 246 * this list that match the specified [logfilter]. If [destructive]
247 * is true, the log entries are removed from the original list.
209 */ 248 */
210 LogEntryList getMatches(CallMatcher logfilter) { 249 LogEntryList getMatches(CallMatcher logfilter, bool destructive) {
211 LogEntryList rtn = 250 LogEntryList rtn =
212 new LogEntryList(new List<LogEntry>(), logfilter.toString()); 251 new LogEntryList(new List<LogEntry>(), logfilter.toString());
213 for (var i = 0; i < logs.length; i++) { 252 for (var i = 0; i < logs.length; i++) {
214 LogEntry entry = logs[i]; 253 LogEntry entry = logs[i];
215 if (logfilter.matches(entry.name, entry.args)) { 254 if (logfilter.matches(entry.name, entry.args)) {
216 rtn.add(entry); 255 rtn.add(entry);
256 if (destructive) {
257 logs.removeRange(i--, 1);
258 }
217 } 259 }
218 } 260 }
219 return rtn; 261 return rtn;
220 } 262 }
221 263
222 /** Apply a unit test [Matcher] to the [LogEntryList]. */ 264 /** Apply a unit test [Matcher] to the [LogEntryList]. */
223 LogEntryList verify(Matcher matcher) { 265 LogEntryList verify(Matcher matcher) {
224 if (_mockFailureHandler == null) { 266 if (_mockFailureHandler == null) {
225 _mockFailureHandler = 267 _mockFailureHandler =
226 new _MockFailureHandler(getOrCreateExpectFailureHandler()); 268 new _MockFailureHandler(getOrCreateExpectFailureHandler());
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
276 /** [calledOnce] matches exactly one call. */ 318 /** [calledOnce] matches exactly one call. */
277 final Matcher calledOnce = const _TimesMatcher(1, 1); 319 final Matcher calledOnce = const _TimesMatcher(1, 1);
278 320
279 /** [calledAtLeastOnce] matches one or more calls. */ 321 /** [calledAtLeastOnce] matches one or more calls. */
280 final Matcher calledAtLeastOnce = const _TimesMatcher(1); 322 final Matcher calledAtLeastOnce = const _TimesMatcher(1);
281 323
282 /** [calledAtMostOnce] matches zero or one call. */ 324 /** [calledAtMostOnce] matches zero or one call. */
283 final Matcher calledAtMostOnce = const _TimesMatcher(0, 1); 325 final Matcher calledAtMostOnce = const _TimesMatcher(0, 1);
284 326
285 /** 327 /**
328 * [_ResultMatcher]s are used to make assertions about the results
329 * of method calls.
Siggi Cherem (dart-lang) 2012/06/28 00:44:40 => of a sequence of method calls? (since this is a
gram 2012/06/28 17:32:54 I don't like 'sequence', even if the logs are temp
330 */
331 class _ResultMatcher extends BaseMatcher {
332 final int action;
333 final value;
334 final int count; // -1 for all, 0 for none, 1 for some.
Siggi Cherem (dart-lang) 2012/06/28 00:44:40 please change to something more self explanatory (
gram 2012/06/28 17:32:54 I used a single value as an enum; it scales better
335
336 const _ResultMatcher(this.action, this.value, this.count);
337
338 bool matches(log) {
339 for (LogEntry entry in log) {
340 // normalize the action; PROXY is like RETURN.
341 int eaction = (entry.action == THROW) ? THROW : RETURN;
342 if (eaction == action && value.matches(entry.value)) {
343 if (count == 0) {
344 return false;
345 } else if (count == 1) {
346 return true;
347 }
348 } else {
349 // Mismatch.
350 if (count == -1) { // We need just one mismatch to fail.
351 return false;
352 }
353 }
354 }
355 // If we get here, then if count is -1 we got all matches and
356 // this is success; otherwise we got all mismatched which is
357 // success for count == 0 and failure for count == 1.
358 return (count != 1);
359 }
Siggi Cherem (dart-lang) 2012/06/28 00:44:40 + line separation between methods (here and below
gram 2012/06/28 17:32:54 Done.
360 Description describe(Description description) {
361 description.add(' to ');
362 description.add(count == -1 ? 'alway ' :
363 (count == 0 ? 'never ' : 'sometimes '));
364 if (action == RETURN || action == PROXY)
365 description.add('return ');
366 else
367 description.add('throw ');
368 return description.addDescriptionOf(value);
369 }
370 Description describeMismatch(log, Description mismatchDescription) {
371 if (count != 1) {
372 mismatchDescription.add(entry.value);
373 for (LogEntry entry in log) {
374 if (entry.action != action || !value.matches(entry.value)) {
375 if (entry.action == RETURN || entry.action == PROXY)
376 mismatchDescription.add('returned ');
377 else
378 mismatchDescription.add('threw ');
379 mismatchDescription.add(entry.value);
380 mismatchDescription.add(' at least once');
381 break;
382 }
383 }
384 } else {
385 mismatchDescription.add('never did');
386 }
387 return mismatchDescription;
388 }
389 }
390
391 /**
392 *[alwaysReturned] asserts that all matching calls to a method returned
393 * a value that matched [value].
394 */
395 Matcher alwaysReturned(value) =>
396 new _ResultMatcher(RETURN, wrapMatcher(value), -1);
397
398 /**
399 *[sometimeReturned] asserts that at least one matching call to a method
400 * returned a value that matched [value].
401 */
402 Matcher sometimeReturned(value) =>
403 new _ResultMatcher(RETURN, wrapMatcher(value), 1);
404
405 /**
406 *[neverReturned] asserts that no matching calls to a method returned
407 * a value that matched [value].
408 */
409 Matcher neverReturned(value) =>
410 new _ResultMatcher(RETURN, wrapMatcher(value), 0);
411
412 /**
413 *[alwaysThrew] asserts that all matching calls to a method threw
414 * a value that matched [value].
415 */
416 Matcher alwaysThrew(value) =>
417 new _ResultMatcher(THROW, wrapMatcher(value), -1);
418
419 /**
420 *[sometimeThrew] asserts that at least one matching call to a method threw
421 * a value that matched [value].
422 */
423 Matcher sometimeThrew(value) =>
424 new _ResultMatcher(THROW, wrapMatcher(value), 1);
425
426 /**
427 *[neverThrew] asserts that no matching call to a method threw
428 * a value that matched [value].
429 */
430 Matcher neverThrew(value) =>
431 new _ResultMatcher(THROW, wrapMatcher(value), 0);
432
433 /**
286 * [Mock] is the base class for all mocked objects, with 434 * [Mock] is the base class for all mocked objects, with
287 * support for basic mocking. 435 * support for basic mocking.
288 * 436 *
289 * To create a mock objects for some class T, create a new class using: 437 * To create a mock objects for some class T, create a new class using:
290 * 438 *
291 * class MockT extends Mock implements T {}; 439 * class MockT extends Mock implements T {};
292 * 440 *
293 * Then specify the behavior of the Mock for different methods using 441 * Then specify the behavior of the Mock for different methods using
294 * [when] (to select the method and parameters) and [thenReturn], 442 * [whenThereAre] (to select the method and parameters) and [thenReturn],
Siggi Cherem (dart-lang) 2012/06/28 00:44:40 whenThereAre feels really verbose, see some sugges
gram 2012/06/28 17:32:54 Done.
295 * [alwaysReturn], [thenThrow] and/or [alwaysThrow]. 443 * [alwaysReturn], [thenThrow], [alwaysThrow], [thenCall] or [alwaysCall].
444 * [thenReturn], [thenThrow] and [thenCall] are one-shot so you would
445 * typically call these more than once to specify a sequence of actions;
446 * this can be done with chained calls, e.g.:
447 *
448 * m.whenThereAre(callsTo('foo')).
449 * thenReturn(0).thenReturn(1).thenReturn(2);
Siggi Cherem (dart-lang) 2012/06/28 00:44:40 you can also use cascades here :) m.whenThereAre(c
gram 2012/06/28 17:32:54 Yeah, but most people are never going to know they
450 *
451 * [thenCall] and [alwaysCall] allow you to proxy mocked methods, chaining
452 * to some other implementation. This provides a way to implement 'spies'.
Siggi Cherem (dart-lang) 2012/06/28 00:44:40 It took me a while to get what are proxies and spi
gram 2012/06/28 17:32:54 I've changed comments in a few places that hopeful
296 * 453 *
297 * You can then use the mock object. Once you are done, to verify the 454 * You can then use the mock object. Once you are done, to verify the
298 * behavior, use [verify] to extract a relevant subset of method call 455 * behavior, use [forThe] to extract a relevant subset of method call
Siggi Cherem (dart-lang) 2012/06/28 00:44:40 :-( I liked verify better... more suggestions on t
299 * logs and apply [Matchers] to these. 456 * logs and apply [Matchers] to these through calling [verify].
300 * 457 *
301 * Limitations: 458 * Limitations:
302 * - only positional parameters are supported (up to 10); 459 * - only positional parameters are supported (up to 10);
303 * - to mock getters you will need to include parentheses. 460 * - to mock getters you will need to include parentheses.
304 * 461 *
305 * Here is a simple example: 462 * Here is a simple example:
306 * 463 *
307 * class MockList extends Mock implements List {}; 464 * class MockList extends Mock implements List {};
308 * 465 *
309 * List m = new MockList(); 466 * List m = new MockList();
310 * m.when('add', anything).alwaysReturn(0); 467 * m.whenThereAre(callsTo('add', anything)).alwaysReturn(0);
311 * 468 *
312 * m.add('foo'); 469 * m.add('foo');
313 * m.add('bar'); 470 * m.add('bar');
314 * 471 *
315 * m.verify('add', anything, was:calledExactly(2)); 472 * m.forThe(callsTo('add', anything)).verify(calledExactly(2));
316 * m.verify('add', 'foo', was:calledOnce); 473 * m.forThe(callsTo('add', 'foo')).verify(calledOnce);
317 * m.verify('add', 'isNull, was:neverCalled); 474 * m.forThe(callsTo('add', 'isNull)).verify(neverCalled);
475 *
476 * Note that we don't need to provide argument matchers for all arguments,
477 * but we do need to provide arguments for all matchers. So this is allowed:
478 *
479 * m.whenThereAre(callsTo('add')).alwaysReturn(0);
480 * m.add(1, 2);
481 *
482 * But this is not allowed and will throw an exception:
483 *
484 * m.whenThereAre(callsTo('add', anything, anything)).alwaysReturn(0);
485 * m.add(1);
486 *
487 * Here is a way to implement a 'spy':
488 *
489 * class Foo {
490 * bar(a, b, c) => a + b + c;
491 * }
492 *
493 * class MockFoo extends Mock implements Foo {
494 * Foo real;
495 * MockFoo() {
496 * real = new Foo();
497 * this.whenThereAre(callsTo('bar')).alwaysCall(real.bar);
498 * }
499 * }
500 *
318 */ 501 */
319 class Mock { 502 class Mock {
320 Map<String,Behavior> behaviors; /** The set of [behavior]s supported. */ 503 Map<String,Behavior> behaviors; /** The set of [behavior]s supported. */
321 LogEntryList log; /** The [log] of calls made. */ 504 LogEntryList log; /** The [log] of calls made. */
322 505
323 Mock() { 506 Mock() {
324 behaviors = new Map<String,Behavior>(); 507 behaviors = new Map<String,Behavior>();
325 log = new LogEntryList(new List<LogEntry>()); 508 log = new LogEntryList(new List<LogEntry>());
326 } 509 }
327 510
328 /** 511 /**
329 * [when] is used to create a new or extend an existing [Behavior]. 512 * [whenThereAre] is used to create a new or extend an existing [Behavior].
330 * The [method] name and the argument [Matcher] is specified. A 513 * A [CallMatcher] [filter] must be supplied, and the [Behavior]s for
331 * corresponding [CallMatcher] is created, and the [Behavior]s for 514 * that signature are returned (being created first if needed).
332 * its signature are returned (being created first if needed). 515 *
516 * The name [whenThereAre] was chosen to flow grammatically with the
517 * typical use case:
518 *
519 * mock.whenThereAre(callsTo(...)).thenAlwaysReturn(...);
333 */ 520 */
334 Behavior when(String method, [ 521 Behavior whenThereAre(CallMatcher filter) {
335 arg0 = _noArg, 522 String key = filter.toString();
336 arg1 = _noArg,
337 arg2 = _noArg,
338 arg3 = _noArg,
339 arg4 = _noArg,
340 arg5 = _noArg,
341 arg6 = _noArg,
342 arg7 = _noArg,
343 arg8 = _noArg,
344 arg9 = _noArg]) {
345 CallMatcher logfilter = new CallMatcher(method,
346 arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
347 String key = logfilter.toString();
348 if (!behaviors.containsKey(key)) { 523 if (!behaviors.containsKey(key)) {
349 Behavior b = new Behavior(logfilter); 524 Behavior b = new Behavior(filter);
350 behaviors[key] = b; 525 behaviors[key] = b;
351 return b; 526 return b;
352 } else { 527 } else {
353 return behaviors[key]; 528 return behaviors[key];
354 } 529 }
355 } 530 }
356 531
357 /** 532 /**
358 * This is the handler for method calls. We loo through the list 533 * This is the handler for method calls. We loo through the list
359 * of [Behavior]s, and find the first match that still has return 534 * of [Behavior]s, and find the first match that still has return
360 * values available, and then do the action specified by that 535 * values available, and then do the action specified by that
361 * return value. If we find no [Behavior] to apply an exception is 536 * return value. If we find no [Behavior] to apply an exception is
362 * thrown. 537 * thrown.
363 */ 538 */
364 noSuchMethod(String name, List args) { 539 noSuchMethod(String name, List args) {
365 for (String k in behaviors.getKeys()) { 540 for (String k in behaviors.getKeys()) {
366 Behavior b = behaviors[k]; 541 Behavior b = behaviors[k];
367 if (b.matches(name, args)) { 542 if (b.matches(name, args)) {
368 List rv = b.returnValues; 543 List actions = b.actions;
369 if (rv == null || rv.length == 0) { 544 if (actions == null || actions.length == 0) {
370 continue; // No return values left in this Behavior. 545 continue; // No return values left in this Behavior.
371 } 546 }
372 // Get the first response. 547 // Get the first response.
373 BehaviorValue bv = rv[0]; 548 BehaviorValue bv = actions[0];
374 // If it is exhausted, remove it from the list. 549 // If it is exhausted, remove it from the list.
375 // Note that for endlessly repeating values, we started the count at 550 // Note that for endlessly repeating values, we started the count at
376 // 0, so we get a potentially useful value here, which is the 551 // 0, so we get a potentially useful value here, which is the
377 // (negation of) the number of times we returned the value. 552 // (negation of) the number of times we returned the value.
378 if (--bv.count == 0) { 553 if (--bv.count == 0) {
379 rv.removeRange(0, 1); 554 actions.removeRange(0, 1);
380 if (rv.length == 0) { 555 if (actions.length == 0) {
381 // Remove the behavior. Note that in the future there 556 // Remove the behavior. Note that in the future there
382 // may be some value in preserving the behaviors for 557 // may be some value in preserving the behaviors for
383 // auditing purposes (e.g. how many times was this behavior used?). 558 // auditing purposes (e.g. how many times was this behavior used?).
384 // If we do decide to keep them and perf is an issue instead of 559 // If we do decide to keep them and perf is an issue instead of
385 // deleting we could move this to a separate list. 560 // deleting we could move this to a separate list.
386 behaviors.remove(k); 561 behaviors.remove(k);
387 } 562 }
388 } 563 }
389 // Log the method call and the response.
390 log.add(new LogEntry(name, args, bv));
391 // Do the response. 564 // Do the response.
392 if (bv.isThrow) { 565 switch (bv.action) {
393 throw bv.value; 566 case RETURN:
394 } else { 567 log.add(new LogEntry(name, args, bv.action, bv.value));
395 return bv.value; 568 return bv.value;
569 case THROW:
570 log.add(new LogEntry(name, args, bv.action, bv.value));
571 throw bv.value;
572 case PROXY:
573 var rtn;
574 switch (args.length) {
575 case 0:
576 rtn = bv.value();
577 break;
578 case 1:
579 rtn = bv.value(args[0]);
580 break;
581 case 2:
582 rtn = bv.value(args[0], args[1]);
583 break;
584 case 3:
585 rtn = bv.value(args[0], args[1], args[2]);
586 break;
587 case 4:
588 rtn = bv.value(args[0], args[1], args[2], args[3]);
589 break;
590 case 5:
591 rtn = bv.value(args[0], args[1], args[2], args[3], args[4]);
592 break;
593 case 6:
594 rtn = bv.value(args[0], args[1], args[2], args[3],
595 args[4], args[5]);
596 break;
597 case 7:
598 rtn = bv.value(args[0], args[1], args[2], args[3],
599 args[4], args[5], args[6]);
600 break;
601 case 8:
602 rtn = bv.value(args[0], args[1], args[2], args[3],
603 args[4], args[5], args[6], args[7]);
604 break;
605 case 9:
606 rtn = bv.value(args[0], args[1], args[2], args[3],
607 args[4], args[5], args[6], args[7], args[8]);
608 break;
609 case 9:
610 rtn = bv.value(args[0], args[1], args[2], args[3],
611 args[4], args[5], args[6], args[7], args[8], args[9]);
612 break;
613 default:
614 throw new Exception(
615 "Cannot proxy calls with more than 10 parameters");
616 }
617 log.add(new LogEntry(name, args, bv.action, rtn));
618 return rtn;
396 } 619 }
397 } 620 }
398 } 621 }
399 throw new Exception('No behavior specified for method $name'); 622 throw new Exception('No behavior specified for method $name');
400 } 623 }
401 624
402 /** 625 /**
403 * [verify] extracts all calls from the object log that match the 626 * [forThe] extracts all calls from the object log that match the
404 * method signature, then applies the [was] matcher. The matching 627 * [logFilter] [CallMatcher], and returns the matching list of
405 * list of [LogEntry]s is returned so that further calls to verify() 628 * [LogEntry]s. If [destructive] is false (the default) the matching
406 * can be chained . 629 * calls are left in the mock object's log, else they are removed.
630 * Removal allows us to verify a set of interactions and then verify
631 * that there are no other interactions left.
632 *
633 * The name [forThe] was chosen to flow grammatically with [callsTo],
634 * as this is usually used in the form:
635 *
636 * mock.forThe(callsTo(...)).verify(...);
407 */ 637 */
408 LogEntryList verify(String method, [ arg0 = _noArg, 638 LogEntryList forThe(CallMatcher logFilter, [bool destructive = false]) {
409 arg1 = _noArg, 639 return log.getMatches(logFilter, destructive);
410 arg2 = _noArg,
411 arg3 = _noArg,
412 arg4 = _noArg,
413 arg5 = _noArg,
414 arg6 = _noArg,
415 arg7 = _noArg,
416 arg8 = _noArg,
417 arg9 = _noArg,
418 Matcher was = calledOnce]) {
419 CallMatcher logfilter = new CallMatcher(method,
420 arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
421 LogEntryList _logs = log.getMatches(logfilter);
422 _logs.verify(was);
423 return _logs;
424 } 640 }
425 641
426 /** [verifyZeroInteractions] returns true if no calls were made */ 642 /** [verifyZeroInteractions] returns true if no calls were made */
427 bool verifyZeroInteractions() => log.logs.length == 0; 643 bool verifyZeroInteractions() => log.logs.length == 0;
428
429 } 644 }
430 645
646
OLDNEW
« no previous file with comments | « no previous file | tests/lib/unittest/unittest_test.dart » ('j') | tests/lib/unittest/unittest_test.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698