blob: a0545f73ab9debda2beb9d668ba977ce91861ef8 [file] [log] [blame]
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Matchers to be used with the mock utilities. They allow for
* flexible matching by type. Custom matchers can be created by passing a
* matcher function into an ArgumentMatcher instance.
*
* For examples, please see the unit test.
*
*/
goog.provide('goog.testing.mockmatchers');
goog.provide('goog.testing.mockmatchers.ArgumentMatcher');
goog.provide('goog.testing.mockmatchers.IgnoreArgument');
goog.provide('goog.testing.mockmatchers.InstanceOf');
goog.provide('goog.testing.mockmatchers.ObjectEquals');
goog.provide('goog.testing.mockmatchers.RegexpMatch');
goog.provide('goog.testing.mockmatchers.SaveArgument');
goog.provide('goog.testing.mockmatchers.TypeOf');
goog.require('goog.array');
goog.require('goog.dom');
goog.require('goog.testing.asserts');
/**
* A simple interface for executing argument matching. A match in this case is
* testing to see if a supplied object fits a given criteria. True is returned
* if the given criteria is met.
* @param {Function=} opt_matchFn A function that evaluates a given argument
* and returns true if it meets a given criteria.
* @param {?string=} opt_matchName The name expressing intent as part of
* an error message for when a match fails.
* @constructor
*/
goog.testing.mockmatchers.ArgumentMatcher =
function(opt_matchFn, opt_matchName) {
/**
* A function that evaluates a given argument and returns true if it meets a
* given criteria.
* @type {Function}
* @private
*/
this.matchFn_ = opt_matchFn || null;
/**
* A string indicating the match intent (e.g. isBoolean or isString).
* @type {?string}
* @private
*/
this.matchName_ = opt_matchName || null;
};
/**
* A function that takes a match argument and an optional MockExpectation
* which (if provided) will get error information and returns whether or
* not it matches.
* @param {*} toVerify The argument that should be verified.
* @param {goog.testing.MockExpectation?=} opt_expectation The expectation
* for this match.
* @return {boolean} Whether or not a given argument passes verification.
*/
goog.testing.mockmatchers.ArgumentMatcher.prototype.matches =
function(toVerify, opt_expectation) {
if (this.matchFn_) {
var isamatch = this.matchFn_(toVerify);
if (!isamatch && opt_expectation) {
if (this.matchName_) {
opt_expectation.addErrorMessage('Expected: ' +
this.matchName_ + ' but was: ' + _displayStringForValue(toVerify));
} else {
opt_expectation.addErrorMessage('Expected: missing mockmatcher' +
' description but was: ' +
_displayStringForValue(toVerify));
}
}
return isamatch;
} else {
throw Error('No match function defined for this mock matcher');
}
};
/**
* A matcher that verifies that an argument is an instance of a given class.
* @param {Function} ctor The class that will be used for verification.
* @constructor
* @extends {goog.testing.mockmatchers.ArgumentMatcher}
* @final
*/
goog.testing.mockmatchers.InstanceOf = function(ctor) {
goog.testing.mockmatchers.ArgumentMatcher.call(this,
function(obj) {
return obj instanceof ctor;
// NOTE: Browser differences on ctor.toString() output
// make using that here problematic. So for now, just let
// people know the instanceOf() failed without providing
// browser specific details...
}, 'instanceOf()');
};
goog.inherits(goog.testing.mockmatchers.InstanceOf,
goog.testing.mockmatchers.ArgumentMatcher);
/**
* A matcher that verifies that an argument is of a given type (e.g. "object").
* @param {string} type The type that a given argument must have.
* @constructor
* @extends {goog.testing.mockmatchers.ArgumentMatcher}
* @final
*/
goog.testing.mockmatchers.TypeOf = function(type) {
goog.testing.mockmatchers.ArgumentMatcher.call(this,
function(obj) {
return goog.typeOf(obj) == type;
}, 'typeOf(' + type + ')');
};
goog.inherits(goog.testing.mockmatchers.TypeOf,
goog.testing.mockmatchers.ArgumentMatcher);
/**
* A matcher that verifies that an argument matches a given RegExp.
* @param {RegExp} regexp The regular expression that the argument must match.
* @constructor
* @extends {goog.testing.mockmatchers.ArgumentMatcher}
* @final
*/
goog.testing.mockmatchers.RegexpMatch = function(regexp) {
goog.testing.mockmatchers.ArgumentMatcher.call(this,
function(str) {
return regexp.test(str);
}, 'match(' + regexp + ')');
};
goog.inherits(goog.testing.mockmatchers.RegexpMatch,
goog.testing.mockmatchers.ArgumentMatcher);
/**
* A matcher that always returns true. It is useful when the user does not care
* for some arguments.
* For example: mockFunction('username', 'password', IgnoreArgument);
* @constructor
* @extends {goog.testing.mockmatchers.ArgumentMatcher}
* @final
*/
goog.testing.mockmatchers.IgnoreArgument = function() {
goog.testing.mockmatchers.ArgumentMatcher.call(this,
function() {
return true;
}, 'true');
};
goog.inherits(goog.testing.mockmatchers.IgnoreArgument,
goog.testing.mockmatchers.ArgumentMatcher);
/**
* A matcher that verifies that the argument is an object that equals the given
* expected object, using a deep comparison.
* @param {Object} expectedObject An object to match against when
* verifying the argument.
* @constructor
* @extends {goog.testing.mockmatchers.ArgumentMatcher}
*/
goog.testing.mockmatchers.ObjectEquals = function(expectedObject) {
goog.testing.mockmatchers.ArgumentMatcher.call(this,
function(matchObject) {
assertObjectEquals('Expected equal objects', expectedObject,
matchObject);
return true;
}, 'objectEquals(' + expectedObject + ')');
};
goog.inherits(goog.testing.mockmatchers.ObjectEquals,
goog.testing.mockmatchers.ArgumentMatcher);
/** @override */
goog.testing.mockmatchers.ObjectEquals.prototype.matches =
function(toVerify, opt_expectation) {
// Override the default matches implementation to capture the exception thrown
// by assertObjectEquals (if any) and add that message to the expectation.
try {
return goog.testing.mockmatchers.ObjectEquals.superClass_.matches.call(
this, toVerify, opt_expectation);
} catch (e) {
if (opt_expectation) {
opt_expectation.addErrorMessage(e.message);
}
return false;
}
};
/**
* A matcher that saves the argument that it is verifying so that your unit test
* can perform extra tests with this argument later. For example, if the
* argument is a callback method, the unit test can then later call this
* callback to test the asynchronous portion of the call.
* @param {goog.testing.mockmatchers.ArgumentMatcher|Function=} opt_matcher
* Argument matcher or matching function that will be used to validate the
* argument. By default, argument will always be valid.
* @param {?string=} opt_matchName The name expressing intent as part of
* an error message for when a match fails.
* @constructor
* @extends {goog.testing.mockmatchers.ArgumentMatcher}
* @final
*/
goog.testing.mockmatchers.SaveArgument = function(opt_matcher, opt_matchName) {
goog.testing.mockmatchers.ArgumentMatcher.call(
this, /** @type {Function} */ (opt_matcher), opt_matchName);
if (opt_matcher instanceof goog.testing.mockmatchers.ArgumentMatcher) {
/**
* Delegate match requests to this matcher.
* @type {goog.testing.mockmatchers.ArgumentMatcher}
* @private
*/
this.delegateMatcher_ = opt_matcher;
} else if (!opt_matcher) {
this.delegateMatcher_ = goog.testing.mockmatchers.ignoreArgument;
}
};
goog.inherits(goog.testing.mockmatchers.SaveArgument,
goog.testing.mockmatchers.ArgumentMatcher);
/** @override */
goog.testing.mockmatchers.SaveArgument.prototype.matches = function(
toVerify, opt_expectation) {
this.arg = toVerify;
if (this.delegateMatcher_) {
return this.delegateMatcher_.matches(toVerify, opt_expectation);
}
return goog.testing.mockmatchers.SaveArgument.superClass_.matches.call(
this, toVerify, opt_expectation);
};
/**
* Saved argument that was verified.
* @type {*}
*/
goog.testing.mockmatchers.SaveArgument.prototype.arg;
/**
* An instance of the IgnoreArgument matcher. Returns true for all matches.
* @type {goog.testing.mockmatchers.IgnoreArgument}
*/
goog.testing.mockmatchers.ignoreArgument =
new goog.testing.mockmatchers.IgnoreArgument();
/**
* A matcher that verifies that an argument is an array.
* @type {goog.testing.mockmatchers.ArgumentMatcher}
*/
goog.testing.mockmatchers.isArray =
new goog.testing.mockmatchers.ArgumentMatcher(goog.isArray,
'isArray');
/**
* A matcher that verifies that an argument is a array-like. A NodeList is an
* example of a collection that is very close to an array.
* @type {goog.testing.mockmatchers.ArgumentMatcher}
*/
goog.testing.mockmatchers.isArrayLike =
new goog.testing.mockmatchers.ArgumentMatcher(goog.isArrayLike,
'isArrayLike');
/**
* A matcher that verifies that an argument is a date-like.
* @type {goog.testing.mockmatchers.ArgumentMatcher}
*/
goog.testing.mockmatchers.isDateLike =
new goog.testing.mockmatchers.ArgumentMatcher(goog.isDateLike,
'isDateLike');
/**
* A matcher that verifies that an argument is a string.
* @type {goog.testing.mockmatchers.ArgumentMatcher}
*/
goog.testing.mockmatchers.isString =
new goog.testing.mockmatchers.ArgumentMatcher(goog.isString,
'isString');
/**
* A matcher that verifies that an argument is a boolean.
* @type {goog.testing.mockmatchers.ArgumentMatcher}
*/
goog.testing.mockmatchers.isBoolean =
new goog.testing.mockmatchers.ArgumentMatcher(goog.isBoolean,
'isBoolean');
/**
* A matcher that verifies that an argument is a number.
* @type {goog.testing.mockmatchers.ArgumentMatcher}
*/
goog.testing.mockmatchers.isNumber =
new goog.testing.mockmatchers.ArgumentMatcher(goog.isNumber,
'isNumber');
/**
* A matcher that verifies that an argument is a function.
* @type {goog.testing.mockmatchers.ArgumentMatcher}
*/
goog.testing.mockmatchers.isFunction =
new goog.testing.mockmatchers.ArgumentMatcher(goog.isFunction,
'isFunction');
/**
* A matcher that verifies that an argument is an object.
* @type {goog.testing.mockmatchers.ArgumentMatcher}
*/
goog.testing.mockmatchers.isObject =
new goog.testing.mockmatchers.ArgumentMatcher(goog.isObject,
'isObject');
/**
* A matcher that verifies that an argument is like a DOM node.
* @type {goog.testing.mockmatchers.ArgumentMatcher}
*/
goog.testing.mockmatchers.isNodeLike =
new goog.testing.mockmatchers.ArgumentMatcher(goog.dom.isNodeLike,
'isNodeLike');
/**
* A function that checks to see if an array matches a given set of
* expectations. The expectations array can be a mix of ArgumentMatcher
* implementations and values. True will be returned if values are identical or
* if a matcher returns a positive result.
* @param {Array<?>} expectedArr An array of expectations which can be either
* values to check for equality or ArgumentMatchers.
* @param {Array<?>} arr The array to match.
* @param {goog.testing.MockExpectation?=} opt_expectation The expectation
* for this match.
* @return {boolean} Whether or not the given array matches the expectations.
*/
goog.testing.mockmatchers.flexibleArrayMatcher =
function(expectedArr, arr, opt_expectation) {
return goog.array.equals(expectedArr, arr, function(a, b) {
var errCount = 0;
if (opt_expectation) {
errCount = opt_expectation.getErrorMessageCount();
}
var isamatch = a === b ||
a instanceof goog.testing.mockmatchers.ArgumentMatcher &&
a.matches(b, opt_expectation);
var failureMessage = null;
if (!isamatch) {
failureMessage = goog.testing.asserts.findDifferences(a, b);
isamatch = !failureMessage;
}
if (!isamatch && opt_expectation) {
// If the error count changed, the match sent out an error
// message. If the error count has not changed, then
// we need to send out an error message...
if (errCount == opt_expectation.getErrorMessageCount()) {
// Use the _displayStringForValue() from assert.js
// for consistency...
if (!failureMessage) {
failureMessage = 'Expected: ' + _displayStringForValue(a) +
' but was: ' + _displayStringForValue(b);
}
opt_expectation.addErrorMessage(failureMessage);
}
}
return isamatch;
});
};