# lib/goog/testing/mockmatchers.js

1 | // Copyright 2008 The Closure Library Authors. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS-IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | /**
16 | * @fileoverview Matchers to be used with the mock utilities. They allow for
17 | * flexible matching by type. Custom matchers can be created by passing a
18 | * matcher function into an ArgumentMatcher instance.
19 | *
20 | * For examples, please see the unit test.
21 | *
22 | */
23 |
24 |
25 | goog.provide('goog.testing.mockmatchers');
26 | goog.provide('goog.testing.mockmatchers.ArgumentMatcher');
27 | goog.provide('goog.testing.mockmatchers.IgnoreArgument');
28 | goog.provide('goog.testing.mockmatchers.InstanceOf');
29 | goog.provide('goog.testing.mockmatchers.ObjectEquals');
30 | goog.provide('goog.testing.mockmatchers.RegexpMatch');
31 | goog.provide('goog.testing.mockmatchers.SaveArgument');
32 | goog.provide('goog.testing.mockmatchers.TypeOf');
33 |
34 | goog.require('goog.array');
35 | goog.require('goog.dom');
36 | goog.require('goog.testing.asserts');
37 |
38 |
39 |
40 | /**
41 | * A simple interface for executing argument matching. A match in this case is
42 | * testing to see if a supplied object fits a given criteria. True is returned
43 | * if the given criteria is met.
44 | * @param {Function=} opt_matchFn A function that evaluates a given argument
45 | * and returns true if it meets a given criteria.
46 | * @param {?string=} opt_matchName The name expressing intent as part of
47 | * an error message for when a match fails.
48 | * @constructor
49 | */
50 | goog.testing.mockmatchers.ArgumentMatcher =
51 | function(opt_matchFn, opt_matchName) {
52 | /**
53 | * A function that evaluates a given argument and returns true if it meets a
54 | * given criteria.
55 | * @type {Function}
56 | * @private
57 | */
58 | this.matchFn_ = opt_matchFn || null;
59 |
60 | /**
61 | * A string indicating the match intent (e.g. isBoolean or isString).
62 | * @type {?string}
63 | * @private
64 | */
65 | this.matchName_ = opt_matchName || null;
66 | };
67 |
68 |
69 | /**
70 | * A function that takes a match argument and an optional MockExpectation
71 | * which (if provided) will get error information and returns whether or
72 | * not it matches.
73 | * @param {*} toVerify The argument that should be verified.
74 | * @param {goog.testing.MockExpectation?=} opt_expectation The expectation
75 | * for this match.
76 | * @return {boolean} Whether or not a given argument passes verification.
77 | */
78 | goog.testing.mockmatchers.ArgumentMatcher.prototype.matches =
79 | function(toVerify, opt_expectation) {
80 | if (this.matchFn_) {
81 | var isamatch = this.matchFn_(toVerify);
82 | if (!isamatch && opt_expectation) {
83 | if (this.matchName_) {
84 | opt_expectation.addErrorMessage('Expected: ' +
85 | this.matchName_ + ' but was: ' + _displayStringForValue(toVerify));
86 | } else {
87 | opt_expectation.addErrorMessage('Expected: missing mockmatcher' +
88 | ' description but was: ' +
89 | _displayStringForValue(toVerify));
90 | }
91 | }
92 | return isamatch;
93 | } else {
94 | throw Error('No match function defined for this mock matcher');
95 | }
96 | };
97 |
98 |
99 |
100 | /**
101 | * A matcher that verifies that an argument is an instance of a given class.
102 | * @param {Function} ctor The class that will be used for verification.
103 | * @constructor
104 | * @extends {goog.testing.mockmatchers.ArgumentMatcher}
105 | * @final
106 | */
107 | goog.testing.mockmatchers.InstanceOf = function(ctor) {
108 | goog.testing.mockmatchers.ArgumentMatcher.call(this,
109 | function(obj) {
110 | return obj instanceof ctor;
111 | // NOTE: Browser differences on ctor.toString() output
112 | // make using that here problematic. So for now, just let
113 | // people know the instanceOf() failed without providing
114 | // browser specific details...
115 | }, 'instanceOf()');
116 | };
117 | goog.inherits(goog.testing.mockmatchers.InstanceOf,
118 | goog.testing.mockmatchers.ArgumentMatcher);
119 |
120 |
121 |
122 | /**
123 | * A matcher that verifies that an argument is of a given type (e.g. "object").
124 | * @param {string} type The type that a given argument must have.
125 | * @constructor
126 | * @extends {goog.testing.mockmatchers.ArgumentMatcher}
127 | * @final
128 | */
129 | goog.testing.mockmatchers.TypeOf = function(type) {
130 | goog.testing.mockmatchers.ArgumentMatcher.call(this,
131 | function(obj) {
132 | return goog.typeOf(obj) == type;
133 | }, 'typeOf(' + type + ')');
134 | };
135 | goog.inherits(goog.testing.mockmatchers.TypeOf,
136 | goog.testing.mockmatchers.ArgumentMatcher);
137 |
138 |
139 |
140 | /**
141 | * A matcher that verifies that an argument matches a given RegExp.
142 | * @param {RegExp} regexp The regular expression that the argument must match.
143 | * @constructor
144 | * @extends {goog.testing.mockmatchers.ArgumentMatcher}
145 | * @final
146 | */
147 | goog.testing.mockmatchers.RegexpMatch = function(regexp) {
148 | goog.testing.mockmatchers.ArgumentMatcher.call(this,
149 | function(str) {
150 | return regexp.test(str);
151 | }, 'match(' + regexp + ')');
152 | };
153 | goog.inherits(goog.testing.mockmatchers.RegexpMatch,
154 | goog.testing.mockmatchers.ArgumentMatcher);
155 |
156 |
157 |
158 | /**
159 | * A matcher that always returns true. It is useful when the user does not care
160 | * for some arguments.
161 | * For example: mockFunction('username', 'password', IgnoreArgument);
162 | * @constructor
163 | * @extends {goog.testing.mockmatchers.ArgumentMatcher}
164 | * @final
165 | */
166 | goog.testing.mockmatchers.IgnoreArgument = function() {
167 | goog.testing.mockmatchers.ArgumentMatcher.call(this,
168 | function() {
169 | return true;
170 | }, 'true');
171 | };
172 | goog.inherits(goog.testing.mockmatchers.IgnoreArgument,
173 | goog.testing.mockmatchers.ArgumentMatcher);
174 |
175 |
176 |
177 | /**
178 | * A matcher that verifies that the argument is an object that equals the given
179 | * expected object, using a deep comparison.
180 | * @param {Object} expectedObject An object to match against when
181 | * verifying the argument.
182 | * @constructor
183 | * @extends {goog.testing.mockmatchers.ArgumentMatcher}
184 | */
185 | goog.testing.mockmatchers.ObjectEquals = function(expectedObject) {
186 | goog.testing.mockmatchers.ArgumentMatcher.call(this,
187 | function(matchObject) {
188 | assertObjectEquals('Expected equal objects', expectedObject,
189 | matchObject);
190 | return true;
191 | }, 'objectEquals(' + expectedObject + ')');
192 | };
193 | goog.inherits(goog.testing.mockmatchers.ObjectEquals,
194 | goog.testing.mockmatchers.ArgumentMatcher);
195 |
196 |
197 | /** @override */
198 | goog.testing.mockmatchers.ObjectEquals.prototype.matches =
199 | function(toVerify, opt_expectation) {
200 | // Override the default matches implementation to capture the exception thrown
201 | // by assertObjectEquals (if any) and add that message to the expectation.
202 | try {
203 | return goog.testing.mockmatchers.ObjectEquals.superClass_.matches.call(
204 | this, toVerify, opt_expectation);
205 | } catch (e) {
206 | if (opt_expectation) {
207 | opt_expectation.addErrorMessage(e.message);
208 | }
209 | return false;
210 | }
211 | };
212 |
213 |
214 |
215 | /**
216 | * A matcher that saves the argument that it is verifying so that your unit test
217 | * can perform extra tests with this argument later. For example, if the
218 | * argument is a callback method, the unit test can then later call this
219 | * callback to test the asynchronous portion of the call.
220 | * @param {goog.testing.mockmatchers.ArgumentMatcher|Function=} opt_matcher
221 | * Argument matcher or matching function that will be used to validate the
222 | * argument. By default, argument will always be valid.
223 | * @param {?string=} opt_matchName The name expressing intent as part of
224 | * an error message for when a match fails.
225 | * @constructor
226 | * @extends {goog.testing.mockmatchers.ArgumentMatcher}
227 | * @final
228 | */
229 | goog.testing.mockmatchers.SaveArgument = function(opt_matcher, opt_matchName) {
230 | goog.testing.mockmatchers.ArgumentMatcher.call(
231 | this, /** @type {Function} */ (opt_matcher), opt_matchName);
232 |
233 | if (opt_matcher instanceof goog.testing.mockmatchers.ArgumentMatcher) {
234 | /**
235 | * Delegate match requests to this matcher.
236 | * @type {goog.testing.mockmatchers.ArgumentMatcher}
237 | * @private
238 | */
239 | this.delegateMatcher_ = opt_matcher;
240 | } else if (!opt_matcher) {
241 | this.delegateMatcher_ = goog.testing.mockmatchers.ignoreArgument;
242 | }
243 | };
244 | goog.inherits(goog.testing.mockmatchers.SaveArgument,
245 | goog.testing.mockmatchers.ArgumentMatcher);
246 |
247 |
248 | /** @override */
249 | goog.testing.mockmatchers.SaveArgument.prototype.matches = function(
250 | toVerify, opt_expectation) {
251 | this.arg = toVerify;
252 | if (this.delegateMatcher_) {
253 | return this.delegateMatcher_.matches(toVerify, opt_expectation);
254 | }
255 | return goog.testing.mockmatchers.SaveArgument.superClass_.matches.call(
256 | this, toVerify, opt_expectation);
257 | };
258 |
259 |
260 | /**
261 | * Saved argument that was verified.
262 | * @type {*}
263 | */
264 | goog.testing.mockmatchers.SaveArgument.prototype.arg;
265 |
266 |
267 | /**
268 | * An instance of the IgnoreArgument matcher. Returns true for all matches.
269 | * @type {goog.testing.mockmatchers.IgnoreArgument}
270 | */
271 | goog.testing.mockmatchers.ignoreArgument =
272 | new goog.testing.mockmatchers.IgnoreArgument();
273 |
274 |
275 | /**
276 | * A matcher that verifies that an argument is an array.
277 | * @type {goog.testing.mockmatchers.ArgumentMatcher}
278 | */
279 | goog.testing.mockmatchers.isArray =
280 | new goog.testing.mockmatchers.ArgumentMatcher(goog.isArray,
281 | 'isArray');
282 |
283 |
284 | /**
285 | * A matcher that verifies that an argument is a array-like. A NodeList is an
286 | * example of a collection that is very close to an array.
287 | * @type {goog.testing.mockmatchers.ArgumentMatcher}
288 | */
289 | goog.testing.mockmatchers.isArrayLike =
290 | new goog.testing.mockmatchers.ArgumentMatcher(goog.isArrayLike,
291 | 'isArrayLike');
292 |
293 |
294 | /**
295 | * A matcher that verifies that an argument is a date-like.
296 | * @type {goog.testing.mockmatchers.ArgumentMatcher}
297 | */
298 | goog.testing.mockmatchers.isDateLike =
299 | new goog.testing.mockmatchers.ArgumentMatcher(goog.isDateLike,
300 | 'isDateLike');
301 |
302 |
303 | /**
304 | * A matcher that verifies that an argument is a string.
305 | * @type {goog.testing.mockmatchers.ArgumentMatcher}
306 | */
307 | goog.testing.mockmatchers.isString =
308 | new goog.testing.mockmatchers.ArgumentMatcher(goog.isString,
309 | 'isString');
310 |
311 |
312 | /**
313 | * A matcher that verifies that an argument is a boolean.
314 | * @type {goog.testing.mockmatchers.ArgumentMatcher}
315 | */
316 | goog.testing.mockmatchers.isBoolean =
317 | new goog.testing.mockmatchers.ArgumentMatcher(goog.isBoolean,
318 | 'isBoolean');
319 |
320 |
321 | /**
322 | * A matcher that verifies that an argument is a number.
323 | * @type {goog.testing.mockmatchers.ArgumentMatcher}
324 | */
325 | goog.testing.mockmatchers.isNumber =
326 | new goog.testing.mockmatchers.ArgumentMatcher(goog.isNumber,
327 | 'isNumber');
328 |
329 |
330 | /**
331 | * A matcher that verifies that an argument is a function.
332 | * @type {goog.testing.mockmatchers.ArgumentMatcher}
333 | */
334 | goog.testing.mockmatchers.isFunction =
335 | new goog.testing.mockmatchers.ArgumentMatcher(goog.isFunction,
336 | 'isFunction');
337 |
338 |
339 | /**
340 | * A matcher that verifies that an argument is an object.
341 | * @type {goog.testing.mockmatchers.ArgumentMatcher}
342 | */
343 | goog.testing.mockmatchers.isObject =
344 | new goog.testing.mockmatchers.ArgumentMatcher(goog.isObject,
345 | 'isObject');
346 |
347 |
348 | /**
349 | * A matcher that verifies that an argument is like a DOM node.
350 | * @type {goog.testing.mockmatchers.ArgumentMatcher}
351 | */
352 | goog.testing.mockmatchers.isNodeLike =
353 | new goog.testing.mockmatchers.ArgumentMatcher(goog.dom.isNodeLike,
354 | 'isNodeLike');
355 |
356 |
357 | /**
358 | * A function that checks to see if an array matches a given set of
359 | * expectations. The expectations array can be a mix of ArgumentMatcher
360 | * implementations and values. True will be returned if values are identical or
361 | * if a matcher returns a positive result.
362 | * @param {Array<?>} expectedArr An array of expectations which can be either
363 | * values to check for equality or ArgumentMatchers.
364 | * @param {Array<?>} arr The array to match.
365 | * @param {goog.testing.MockExpectation?=} opt_expectation The expectation
366 | * for this match.
367 | * @return {boolean} Whether or not the given array matches the expectations.
368 | */
369 | goog.testing.mockmatchers.flexibleArrayMatcher =
370 | function(expectedArr, arr, opt_expectation) {
371 | return goog.array.equals(expectedArr, arr, function(a, b) {
372 | var errCount = 0;
373 | if (opt_expectation) {
374 | errCount = opt_expectation.getErrorMessageCount();
375 | }
376 | var isamatch = a === b ||
377 | a instanceof goog.testing.mockmatchers.ArgumentMatcher &&
378 | a.matches(b, opt_expectation);
379 | var failureMessage = null;
380 | if (!isamatch) {
381 | failureMessage = goog.testing.asserts.findDifferences(a, b);
382 | isamatch = !failureMessage;
383 | }
384 | if (!isamatch && opt_expectation) {
385 | // If the error count changed, the match sent out an error
386 | // message. If the error count has not changed, then
387 | // we need to send out an error message...
388 | if (errCount == opt_expectation.getErrorMessageCount()) {
389 | // Use the _displayStringForValue() from assert.js
390 | // for consistency...
391 | if (!failureMessage) {
392 | failureMessage = 'Expected: ' + _displayStringForValue(a) +
393 | ' but was: ' + _displayStringForValue(b);
394 | }
395 | opt_expectation.addErrorMessage(failureMessage);
396 | }
397 | }
398 | return isamatch;
399 | });
400 | };