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 /** | |
| 6 * A simple mocking/spy library. | |
| 7 * | |
| 8 * To create a mock objects for some class T, create a new class using: | |
| 9 * | |
| 10 * class MockT extends Mock implements T {}; | |
| 11 * | |
| 12 * Then specify the [Behavior] of the Mock for different methods using | |
| 13 * [when] (to select the method and parameters) and then the [Action]s | |
| 14 * for the [Behavior] by calling [thenReturn], [alwaysReturn], [thenThrow], | |
| 15 * [alwaysThrow], [thenCall] or [alwaysCall]. | |
| 16 * | |
| 17 * [thenReturn], [thenThrow] and [thenCall] are one-shot so you would | |
| 18 * typically call these more than once to specify a sequence of actions; | |
| 19 * this can be done with chained calls, e.g.: | |
| 20 * | |
| 21 * m.when(callsTo('foo')). | |
| 22 * thenReturn(0).thenReturn(1).thenReturn(2); | |
| 23 * | |
| 24 * [thenCall] and [alwaysCall] allow you to proxy mocked methods, chaining | |
| 25 * to some other implementation. This provides a way to implement 'spies'. | |
| 26 * | |
| 27 * You can disable logging for a particular [Behavior] easily: | |
| 28 * | |
| 29 * m.when(callsTo('bar')).logging = false; | |
| 30 * | |
| 31 * You can then use the mock object. Once you are done, to verify the | |
| 32 * behavior, use [getLogs] to extract a relevant subset of method call | |
| 33 * logs and apply [Matchers] to these through calling [verify]. | |
| 34 * | |
| 35 * A Mock can be given a name when constructed. In this case instead of | |
| 36 * keeping its own log, it uses a shared log. This can be useful to get an | |
| 37 * audit trail of interleaved behavior. It is the responsibility of the user | |
| 38 * to ensure that mock names, if used, are unique. | |
| 39 * | |
| 40 * Limitations: | |
| 41 * - only positional parameters are supported (up to 10); | |
|
Siggi Cherem (dart-lang)
2012/11/09 03:23:31
Not sure if this will render correctly as a list,
| |
| 42 * - to mock getters you will need to include parentheses in the call | |
| 43 * (e.g. m.length() will work but not m.length). | |
| 44 * | |
| 45 * Here is a simple example: | |
| 46 * | |
| 47 * class MockList extends Mock implements List {}; | |
| 48 * | |
| 49 * List m = new MockList(); | |
| 50 * m.when(callsTo('add', anything)).alwaysReturn(0); | |
| 51 * | |
| 52 * m.add('foo'); | |
| 53 * m.add('bar'); | |
| 54 * | |
| 55 * getLogs(m, callsTo('add', anything)).verify(happenedExactly(2)); | |
| 56 * getLogs(m, callsTo('add', 'foo')).verify(happenedOnce); | |
| 57 * getLogs(m, callsTo('add', 'isNull)).verify(neverHappened); | |
| 58 * | |
| 59 * Note that we don't need to provide argument matchers for all arguments, | |
| 60 * but we do need to provide arguments for all matchers. So this is allowed: | |
| 61 * | |
| 62 * m.when(callsTo('add')).alwaysReturn(0); | |
| 63 * m.add(1, 2); | |
| 64 * | |
| 65 * But this is not allowed and will throw an exception: | |
| 66 * | |
| 67 * m.when(callsTo('add', anything, anything)).alwaysReturn(0); | |
| 68 * m.add(1); | |
| 69 * | |
| 70 * Here is a way to implement a 'spy', which is where we log the call | |
| 71 * but then hand it off to some other function, which is the same | |
| 72 * method in a real instance of the class being mocked: | |
| 73 * | |
| 74 * class Foo { | |
| 75 * bar(a, b, c) => a + b + c; | |
| 76 * } | |
| 77 * | |
| 78 * class MockFoo extends Mock implements Foo { | |
| 79 * Foo real; | |
| 80 * MockFoo() { | |
| 81 * real = new Foo(); | |
| 82 * this.when(callsTo('bar')).alwaysCall(real.bar); | |
| 83 * } | |
| 84 * } | |
| 85 * | |
| 86 */ | |
| 87 | |
| 5 library mock; | 88 library mock; |
| 6 import 'matcher.dart'; | 89 import 'matcher.dart'; |
| 7 | 90 |
| 8 /** | 91 /** |
| 9 * The error formatter for mocking is a bit different from the default one | 92 * The error formatter for mocking is a bit different from the default one |
| 10 * for unit testing; instead of the third argument being a 'reason' | 93 * for unit testing; instead of the third argument being a 'reason' |
| 11 * it is instead a [signature] describing the method signature filter | 94 * it is instead a [signature] describing the method signature filter |
| 12 * that was used to select the logs that were verified. | 95 * that was used to select the logs that were verified. |
| 13 */ | 96 */ |
| 14 String _mockingErrorFormatter(actual, Matcher matcher, String signature, | 97 String _mockingErrorFormatter(actual, Matcher matcher, String signature, |
| (...skipping 1094 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1109 /** | 1192 /** |
| 1110 *[neverThrew] asserts that no matching call to a method threw | 1193 *[neverThrew] asserts that no matching call to a method threw |
| 1111 * a value that matched [value]. | 1194 * a value that matched [value]. |
| 1112 */ | 1195 */ |
| 1113 Matcher neverThrew(value) => | 1196 Matcher neverThrew(value) => |
| 1114 new _ResultSetMatcher(Action.THROW, wrapMatcher(value), _Frequency.NONE); | 1197 new _ResultSetMatcher(Action.THROW, wrapMatcher(value), _Frequency.NONE); |
| 1115 | 1198 |
| 1116 /** The shared log used for named mocks. */ | 1199 /** The shared log used for named mocks. */ |
| 1117 LogEntryList sharedLog = null; | 1200 LogEntryList sharedLog = null; |
| 1118 | 1201 |
| 1119 /** | 1202 /** The base class for all mocked objects. */ |
| 1120 * [Mock] is the base class for all mocked objects, with | |
| 1121 * support for basic mocking. | |
| 1122 * | |
| 1123 * To create a mock objects for some class T, create a new class using: | |
| 1124 * | |
| 1125 * class MockT extends Mock implements T {}; | |
| 1126 * | |
| 1127 * Then specify the [Behavior] of the Mock for different methods using | |
| 1128 * [when] (to select the method and parameters) and then the [Action]s | |
| 1129 * for the [Behavior] by calling [thenReturn], [alwaysReturn], [thenThrow], | |
| 1130 * [alwaysThrow], [thenCall] or [alwaysCall]. | |
| 1131 * | |
| 1132 * [thenReturn], [thenThrow] and [thenCall] are one-shot so you would | |
| 1133 * typically call these more than once to specify a sequence of actions; | |
| 1134 * this can be done with chained calls, e.g.: | |
| 1135 * | |
| 1136 * m.when(callsTo('foo')). | |
| 1137 * thenReturn(0).thenReturn(1).thenReturn(2); | |
| 1138 * | |
| 1139 * [thenCall] and [alwaysCall] allow you to proxy mocked methods, chaining | |
| 1140 * to some other implementation. This provides a way to implement 'spies'. | |
| 1141 * | |
| 1142 * You can disable logging for a particular [Behavior] easily: | |
| 1143 * | |
| 1144 * m.when(callsTo('bar')).logging = false; | |
| 1145 * | |
| 1146 * You can then use the mock object. Once you are done, to verify the | |
| 1147 * behavior, use [getLogs] to extract a relevant subset of method call | |
| 1148 * logs and apply [Matchers] to these through calling [verify]. | |
| 1149 * | |
| 1150 * A Mock can be given a name when constructed. In this case instead of | |
| 1151 * keeping its own log, it uses a shared log. This can be useful to get an | |
| 1152 * audit trail of interleaved behavior. It is the responsibility of the user | |
| 1153 * to ensure that mock names, if used, are unique. | |
| 1154 * | |
| 1155 * Limitations: | |
| 1156 * - only positional parameters are supported (up to 10); | |
| 1157 * - to mock getters you will need to include parentheses in the call | |
| 1158 * (e.g. m.length() will work but not m.length). | |
| 1159 * | |
| 1160 * Here is a simple example: | |
| 1161 * | |
| 1162 * class MockList extends Mock implements List {}; | |
| 1163 * | |
| 1164 * List m = new MockList(); | |
| 1165 * m.when(callsTo('add', anything)).alwaysReturn(0); | |
| 1166 * | |
| 1167 * m.add('foo'); | |
| 1168 * m.add('bar'); | |
| 1169 * | |
| 1170 * getLogs(m, callsTo('add', anything)).verify(happenedExactly(2)); | |
| 1171 * getLogs(m, callsTo('add', 'foo')).verify(happenedOnce); | |
| 1172 * getLogs(m, callsTo('add', 'isNull)).verify(neverHappened); | |
| 1173 * | |
| 1174 * Note that we don't need to provide argument matchers for all arguments, | |
| 1175 * but we do need to provide arguments for all matchers. So this is allowed: | |
| 1176 * | |
| 1177 * m.when(callsTo('add')).alwaysReturn(0); | |
| 1178 * m.add(1, 2); | |
| 1179 * | |
| 1180 * But this is not allowed and will throw an exception: | |
| 1181 * | |
| 1182 * m.when(callsTo('add', anything, anything)).alwaysReturn(0); | |
| 1183 * m.add(1); | |
| 1184 * | |
| 1185 * Here is a way to implement a 'spy', which is where we log the call | |
| 1186 * but then hand it off to some other function, which is the same | |
| 1187 * method in a real instance of the class being mocked: | |
| 1188 * | |
| 1189 * class Foo { | |
| 1190 * bar(a, b, c) => a + b + c; | |
| 1191 * } | |
| 1192 * | |
| 1193 * class MockFoo extends Mock implements Foo { | |
| 1194 * Foo real; | |
| 1195 * MockFoo() { | |
| 1196 * real = new Foo(); | |
| 1197 * this.when(callsTo('bar')).alwaysCall(real.bar); | |
| 1198 * } | |
| 1199 * } | |
| 1200 * | |
| 1201 */ | |
| 1202 class Mock { | 1203 class Mock { |
| 1203 /** The mock name. Needed if the log is shared; optional otherwise. */ | 1204 /** The mock name. Needed if the log is shared; optional otherwise. */ |
| 1204 final String name; | 1205 final String name; |
| 1205 | 1206 |
| 1206 /** The set of [Behavior]s supported. */ | 1207 /** The set of [Behavior]s supported. */ |
| 1207 Map<String,Behavior> _behaviors; | 1208 Map<String,Behavior> _behaviors; |
| 1208 | 1209 |
| 1209 /** The [log] of calls made. Only used if [name] is null. */ | 1210 /** The [log] of calls made. Only used if [name] is null. */ |
| 1210 LogEntryList log; | 1211 LogEntryList log; |
| 1211 | 1212 |
| (...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1462 } | 1463 } |
| 1463 } | 1464 } |
| 1464 } | 1465 } |
| 1465 | 1466 |
| 1466 /** Clear both logs and behavior. */ | 1467 /** Clear both logs and behavior. */ |
| 1467 void reset() { | 1468 void reset() { |
| 1468 resetBehavior(); | 1469 resetBehavior(); |
| 1469 clearLogs(); | 1470 clearLogs(); |
| 1470 } | 1471 } |
| 1471 } | 1472 } |
| OLD | NEW |