mockmatchers.js 13 KB

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