Path: blob/trunk/third_party/closure/goog/labs/mock/mock.js
2868 views
// Copyright 2012 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Provides a mocking framework in Closure to make unit tests easy16* to write and understand. The methods provided here can be used to replace17* implementations of existing objects with 'mock' objects to abstract out18* external services and dependencies thereby isolating the code under test.19* Apart from mocking, methods are also provided to just monitor calls to an20* object (spying) and returning specific values for some or all the inputs to21* methods (stubbing).22*23* Design doc : http://go/closuremock24*25*/262728goog.provide('goog.labs.mock');29goog.provide('goog.labs.mock.VerificationError');3031goog.require('goog.array');32goog.require('goog.asserts');33goog.require('goog.debug');34goog.require('goog.debug.Error');35goog.require('goog.functions');36goog.require('goog.labs.mock.verification');37goog.require('goog.labs.mock.verification.VerificationMode');38goog.require('goog.object');3940goog.setTestOnly('goog.labs.mock');414243/**44* Mocks a given object or class.45*46* @param {!Object} objectOrClass An instance or a constructor of a class to be47* mocked.48* @return {!Object} The mocked object.49*/50goog.labs.mock.mock = function(objectOrClass) {51// Go over properties of 'objectOrClass' and create a MockManager to52// be used for stubbing out calls to methods.53var mockObjectManager = new goog.labs.mock.MockObjectManager_(objectOrClass);54var mockedObject = mockObjectManager.getMockedItem();55goog.asserts.assertObject(mockedObject);56return /** @type {!Object} */ (mockedObject);57};585960/**61* Mocks a given function.62*63* @param {!Function} func A function to be mocked.64* @return {!Function} The mocked function.65*/66goog.labs.mock.mockFunction = function(func) {67var mockFuncManager = new goog.labs.mock.MockFunctionManager_(func);68var mockedFunction = mockFuncManager.getMockedItem();69goog.asserts.assertFunction(mockedFunction);70return /** @type {!Function} */ (mockedFunction);71};727374/**75* Mocks a given constructor.76*77* @param {!Function} ctor A constructor function to be mocked.78* @return {!Function} The mocked constructor.79*/80goog.labs.mock.mockConstructor = function(ctor) {81var mockCtor = goog.labs.mock.mockFunction(ctor);8283// Copy class members from the real constructor to the mock. Do not copy84// the closure superClass_ property (see goog.inherits), the built-in85// prototype property, or properties added to Function.prototype86for (var property in ctor) {87if (property != 'superClass_' && property != 'prototype' &&88ctor.hasOwnProperty(property)) {89mockCtor[property] = ctor[property];90}91}92return mockCtor;93};949596/**97* Spies on a given object.98*99* @param {!Object} obj The object to be spied on.100* @return {!Object} The spy object.101*/102goog.labs.mock.spy = function(obj) {103// Go over properties of 'obj' and create a MockSpyManager_ to104// be used for spying on calls to methods.105var mockSpyManager = new goog.labs.mock.MockSpyManager_(obj);106var spyObject = mockSpyManager.getMockedItem();107goog.asserts.assert(spyObject);108return spyObject;109};110111112/**113* Returns an object that can be used to verify calls to specific methods of a114* given mock.115*116* @param {!Object} obj The mocked object.117* @param {!goog.labs.mock.verification.VerificationMode=} opt_verificationMode The mode118* under which to verify invocations.119* @return {!Object} The verifier.120*/121goog.labs.mock.verify = function(obj, opt_verificationMode) {122var mode = opt_verificationMode || goog.labs.mock.verification.atLeast(1);123obj.$verificationModeSetter(mode);124125return obj.$callVerifier;126};127128129/**130* Returns a name to identify a function. Named functions return their names,131* unnamed functions return a string of the form '#anonymous{ID}' where ID is132* a unique identifier for each anonymous function.133* @private134* @param {!Function} func The function.135* @return {string} The function name.136*/137goog.labs.mock.getFunctionName_ = function(func) {138var funcName = goog.debug.getFunctionName(func);139if (funcName == '' || funcName == '[Anonymous]') {140funcName = '#anonymous' + goog.labs.mock.getUid(func);141}142return funcName;143};144145146/**147* Returns a nicely formatted, readble representation of a method call.148* @private149* @param {string} methodName The name of the method.150* @param {Array<?>=} opt_args The method arguments.151* @return {string} The string representation of the method call.152*/153goog.labs.mock.formatMethodCall_ = function(methodName, opt_args) {154opt_args = opt_args || [];155opt_args = goog.array.map(opt_args, function(arg) {156if (goog.isFunction(arg)) {157var funcName = goog.labs.mock.getFunctionName_(arg);158return '<function ' + funcName + '>';159} else {160var isObjectWithClass = goog.isObject(arg) && !goog.isFunction(arg) &&161!goog.isArray(arg) && arg.constructor != Object;162163if (isObjectWithClass) {164return arg.toString();165}166167return goog.labs.mock.formatValue_(arg);168}169});170return methodName + '(' + opt_args.join(', ') + ')';171};172173174/**175* An array to store objects for unique id generation.176* @private177* @type {!Array<!Object>}178*/179goog.labs.mock.uid_ = [];180181182/**183* A unique Id generator that does not modify the object.184* @param {Object!} obj The object whose unique ID we want to generate.185* @return {number} an unique id for the object.186*/187goog.labs.mock.getUid = function(obj) {188var index = goog.array.indexOf(goog.labs.mock.uid_, obj);189if (index == -1) {190index = goog.labs.mock.uid_.length;191goog.labs.mock.uid_.push(obj);192}193return index;194};195196197/**198* This is just another implementation of goog.debug.deepExpose with a more199* compact format.200* @private201* @param {*} obj The object whose string representation will be returned.202* @param {boolean=} opt_id Whether to include the id of objects or not.203* Defaults to true.204* @return {string} The string representation of the object.205*/206goog.labs.mock.formatValue_ = function(obj, opt_id) {207var id = goog.isDef(opt_id) ? opt_id : true;208var previous = [];209var output = [];210211var helper = function(obj) {212var indentMultiline = function(output) {213return output.replace(/\n/g, '\n');214};215216217try {218if (!goog.isDef(obj)) {219output.push('undefined');220} else if (goog.isNull(obj)) {221output.push('NULL');222} else if (goog.isString(obj)) {223output.push('"' + indentMultiline(obj) + '"');224} else if (goog.isFunction(obj)) {225var funcName = goog.labs.mock.getFunctionName_(obj);226output.push('<function ' + funcName + '>');227} else if (goog.isObject(obj)) {228if (goog.array.contains(previous, obj)) {229if (id) {230output.push(231'<recursive/dupe obj_' + goog.labs.mock.getUid(obj) + '>');232} else {233output.push('<recursive/dupe>');234}235} else {236previous.push(obj);237output.push('{');238for (var x in obj) {239output.push(' ');240output.push(241'"' + x + '"' +242':');243helper(obj[x]);244}245if (id) {246output.push(' _id:' + goog.labs.mock.getUid(obj));247}248output.push('}');249}250} else {251output.push(obj);252}253} catch (e) {254output.push('*** ' + e + ' ***');255}256};257258helper(obj);259return output.join('')260.replace(/"closure_uid_\d+"/g, '_id')261.replace(/{ /g, '{');262263};264265266267/**268* Error thrown when verification failed.269*270* @param {Array<!goog.labs.mock.MethodBinding_>} recordedCalls271* The recorded calls that didn't match the expectation.272* @param {string} methodName The expected method call.273* @param {!goog.labs.mock.verification.VerificationMode} verificationMode The274* expected verification mode which failed verification.275* @param {!Array<?>} args The expected arguments.276* @constructor277* @extends {goog.debug.Error}278* @final279*/280goog.labs.mock.VerificationError = function(281recordedCalls, methodName, verificationMode, args) {282var msg = goog.labs.mock.VerificationError.getVerificationErrorMsg_(283recordedCalls, methodName, verificationMode, args);284goog.labs.mock.VerificationError.base(this, 'constructor', msg);285};286goog.inherits(goog.labs.mock.VerificationError, goog.debug.Error);287288289/** @override */290goog.labs.mock.VerificationError.prototype.name = 'VerificationError';291292293/**294* This array contains the name of the functions that are part of the base295* Object prototype.296* Basically a copy of goog.object.PROTOTYPE_FIELDS_.297* @const298* @type {!Array<string>}299* @private300*/301goog.labs.mock.PROTOTYPE_FIELDS_ = [302'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',303'toLocaleString', 'toString', 'valueOf'304];305306307/**308* Constructs a descriptive error message for an expected method call.309* @private310* @param {Array<!goog.labs.mock.MethodBinding_>} recordedCalls311* The recorded calls that didn't match the expectation.312* @param {string} methodName The expected method call.313* @param {!goog.labs.mock.verification.VerificationMode} verificationMode The314* expected verification mode that failed verification.315* @param {!Array<?>} args The expected arguments.316* @return {string} The error message.317*/318goog.labs.mock.VerificationError.getVerificationErrorMsg_ = function(319recordedCalls, methodName, verificationMode, args) {320321recordedCalls = goog.array.filter(recordedCalls, function(binding) {322return binding.getMethodName() == methodName;323});324325var expected = goog.labs.mock.formatMethodCall_(methodName, args);326327var msg =328'\nExpected: ' + expected.toString() + ' ' + verificationMode.describe();329msg += '\nRecorded: ';330331if (recordedCalls.length > 0) {332msg += recordedCalls.join(',\n ');333} else {334msg += 'No recorded calls';335}336337return msg;338};339340341342/**343* Base class that provides basic functionality for creating, adding and344* finding bindings, offering an executor method that is called when a call to345* the stub is made, an array to hold the bindings and the mocked item, among346* other things.347*348* @constructor349* @struct350* @private351*/352goog.labs.mock.MockManager_ = function() {353/**354* Proxies the methods for the mocked object or class to execute the stubs.355* @type {!Object}356* @protected357*/358this.mockedItem = {};359360/**361* A reference to the object or function being mocked.362* @type {Object|Function}363* @protected364*/365this.mockee = null;366367/**368* Holds the stub bindings established so far.369* @protected370*/371this.methodBindings = [];372373/**374* Holds a reference to the binder used to define stubs.375* @protected376*/377this.$stubBinder = null;378379/**380* Record method calls with no stub definitions.381* @type {!Array<!goog.labs.mock.MethodBinding_>}382* @private383*/384this.callRecords_ = [];385386/**387* Which {@code VerificationMode} to use during verification.388* @private389*/390this.verificationMode_ = goog.labs.mock.verification.atLeast(1);391};392393394/**395* Allows callers of {@code #verify} to override the default verification396* mode of this MockManager.397*398* @param {!goog.labs.mock.verification.VerificationMode} verificationMode399* @private400*/401goog.labs.mock.MockManager_.prototype.setVerificationMode_ = function(402verificationMode) {403this.verificationMode_ = verificationMode;404};405406407/**408* Handles the first step in creating a stub, returning a stub-binder that409* is later used to bind a stub for a method.410*411* @param {string} methodName The name of the method being bound.412* @param {...*} var_args The arguments to the method.413* @return {!goog.labs.mock.StubBinder} The stub binder.414* @private415*/416goog.labs.mock.MockManager_.prototype.handleMockCall_ = function(417methodName, var_args) {418var args = goog.array.slice(arguments, 1);419return new goog.labs.mock.StubBinderImpl_(this, methodName, args);420};421422423/**424* Returns the mock object. This should have a stubbed method for each method425* on the object being mocked.426*427* @return {!Object|!Function} The mock object.428*/429goog.labs.mock.MockManager_.prototype.getMockedItem = function() {430return this.mockedItem;431};432433434/**435* Adds a binding for the method name and arguments to be stubbed.436*437* @param {?string} methodName The name of the stubbed method.438* @param {!Array<?>} args The arguments passed to the method.439* @param {!Function} func The stub function.440* @return {!Array<?>} The array of stubs for further sequential stubs to be441* appended.442*/443goog.labs.mock.MockManager_.prototype.addBinding = function(444methodName, args, func) {445var binding = new goog.labs.mock.MethodBinding_(methodName, args, func);446var sequentialStubsArray = [binding];447goog.array.insertAt(this.methodBindings, sequentialStubsArray, 0);448return sequentialStubsArray;449};450451452/**453* Returns a stub, if defined, for the method name and arguments passed in.454* If there are multiple stubs for this method name and arguments, then455* the most recent binding will be used.456*457* If the next binding is a sequence of stubs, then they'll be returned458* in order until only one is left, at which point it will be returned for every459* subsequent call.460*461* @param {string} methodName The name of the stubbed method.462* @param {!Array<?>} args The arguments passed to the method.463* @return {?Function} The stub function or null.464* @protected465*/466goog.labs.mock.MockManager_.prototype.getNextBinding = function(467methodName, args) {468var bindings = goog.array.find(this.methodBindings, function(bindingArray) {469return bindingArray[0].matches(470methodName, args, false /* isVerification */);471});472if (bindings == null) {473return null;474}475476if (bindings.length > 1) {477return bindings.shift().getStub();478}479return bindings[0].getStub();480};481482483/**484* Returns a stub, if defined, for the method name and arguments passed in as485* parameters.486*487* @param {string} methodName The name of the stubbed method.488* @param {!Array<?>} args The arguments passed to the method.489* @return {Function} The stub function or undefined.490* @protected491*/492goog.labs.mock.MockManager_.prototype.getExecutor = function(methodName, args) {493return this.getNextBinding(methodName, args);494};495496497/**498* Looks up the list of stubs defined on the mock object and executes the499* function associated with that stub.500*501* @param {string} methodName The name of the method to execute.502* @param {...*} var_args The arguments passed to the method.503* @return {*} Value returned by the stub function.504* @protected505*/506goog.labs.mock.MockManager_.prototype.executeStub = function(507methodName, var_args) {508var args = goog.array.slice(arguments, 1);509510// Record this call511this.recordCall_(methodName, args);512513var func = this.getExecutor(methodName, args);514if (func) {515return func.apply(null, args);516}517};518519520/**521* Records a call to 'methodName' with arguments 'args'.522*523* @param {string} methodName The name of the called method.524* @param {!Array<?>} args The array of arguments.525* @private526*/527goog.labs.mock.MockManager_.prototype.recordCall_ = function(methodName, args) {528var callRecord =529new goog.labs.mock.MethodBinding_(methodName, args, goog.nullFunction);530531this.callRecords_.push(callRecord);532};533534535/**536* Verify invocation of a method with specific arguments.537*538* @param {string} methodName The name of the method.539* @param {...*} var_args The arguments passed.540* @protected541*/542goog.labs.mock.MockManager_.prototype.verifyInvocation = function(543methodName, var_args) {544var args = goog.array.slice(arguments, 1);545var count = goog.array.count(this.callRecords_, function(binding) {546return binding.matches(methodName, args, true /* isVerification */);547});548549if (!this.verificationMode_.verify(count)) {550throw new goog.labs.mock.VerificationError(551this.callRecords_, methodName, this.verificationMode_, args);552}553};554555556557/**558* Sets up mock for the given object (or class), stubbing out all the defined559* methods. By default, all stubs return {@code undefined}, though stubs can be560* later defined using {@code goog.labs.mock.when}.561*562* @param {!Object|!Function} objOrClass The object or class to set up the mock563* for. A class is a constructor function.564*565* @constructor566* @struct567* @extends {goog.labs.mock.MockManager_}568* @private569*/570goog.labs.mock.MockObjectManager_ = function(objOrClass) {571goog.labs.mock.MockObjectManager_.base(this, 'constructor');572573/**574* Proxies the calls to establish the first step of the stub bindings (object575* and method name)576* @private577*/578this.objectStubBinder_ = {};579580this.mockee = objOrClass;581582/**583* The call verifier is used to verify the calls. It maps property names to584* the method that does call verification.585* @type {!Object<string, function(string, ...)>}586* @private587*/588this.objectCallVerifier_ = {};589590var obj;591if (goog.isFunction(objOrClass)) {592// Create a temporary subclass with a no-op constructor so that we can593// create an instance and determine what methods it has.594/**595* @constructor596* @final597*/598var tempCtor = function() {};599goog.inherits(tempCtor, objOrClass);600obj = new tempCtor();601} else {602obj = objOrClass;603}604605// Put the object being mocked in the prototype chain of the mock so that606// it has all the correct properties and instanceof works.607/**608* @constructor609* @final610*/611var mockedItemCtor = function() {};612mockedItemCtor.prototype = obj;613this.mockedItem = new mockedItemCtor();614615var enumerableProperties = goog.object.getAllPropertyNames(obj);616// The non enumerable properties are added due to the fact that IE8 does not617// enumerate any of the prototype Object functions even when overriden and618// mocking these is sometimes needed.619for (var i = 0; i < goog.labs.mock.PROTOTYPE_FIELDS_.length; i++) {620var prop = goog.labs.mock.PROTOTYPE_FIELDS_[i];621if (!goog.array.contains(enumerableProperties, prop)) {622enumerableProperties.push(prop);623}624}625626// Adds the properties to the mock, creating a proxy stub for each method on627// the instance.628for (var i = 0; i < enumerableProperties.length; i++) {629var prop = enumerableProperties[i];630if (goog.isFunction(obj[prop])) {631this.mockedItem[prop] = goog.bind(this.executeStub, this, prop);632// The stub binder used to create bindings.633this.objectStubBinder_[prop] =634goog.bind(this.handleMockCall_, this, prop);635// The verifier verifies the calls.636this.objectCallVerifier_[prop] =637goog.bind(this.verifyInvocation, this, prop);638}639}640// The alias for stub binder exposed to the world.641this.mockedItem.$stubBinder = this.objectStubBinder_;642643// The alias for verifier for the world.644this.mockedItem.$callVerifier = this.objectCallVerifier_;645646this.mockedItem.$verificationModeSetter =647goog.bind(this.setVerificationMode_, this);648};649goog.inherits(goog.labs.mock.MockObjectManager_, goog.labs.mock.MockManager_);650651652653/**654* Sets up the spying behavior for the given object.655*656* @param {!Object} obj The object to be spied on.657*658* @constructor659* @struct660* @extends {goog.labs.mock.MockObjectManager_}661* @private662*/663goog.labs.mock.MockSpyManager_ = function(obj) {664goog.labs.mock.MockSpyManager_.base(this, 'constructor', obj);665};666goog.inherits(667goog.labs.mock.MockSpyManager_, goog.labs.mock.MockObjectManager_);668669670/**671* Return a stub, if defined, for the method and arguments passed in. If we lack672* a stub, instead look for a call record that matches the method and arguments.673*674* @return {!Function} The stub or the invocation logger, if defined.675* @override676*/677goog.labs.mock.MockSpyManager_.prototype.getNextBinding = function(678methodName, args) {679var stub = goog.labs.mock.MockSpyManager_.base(680this, 'getNextBinding', methodName, args);681682if (!stub) {683stub = goog.bind(this.mockee[methodName], this.mockee);684}685686return stub;687};688689690691/**692* Sets up mock for the given function, stubbing out. By default, all stubs693* return {@code undefined}, though stubs can be later defined using694* {@code goog.labs.mock.when}.695*696* @param {!Function} func The function to set up the mock for.697*698* @constructor699* @struct700* @extends {goog.labs.mock.MockManager_}701* @private702*/703goog.labs.mock.MockFunctionManager_ = function(func) {704goog.labs.mock.MockFunctionManager_.base(this, 'constructor');705706this.func_ = func;707708/**709* The stub binder used to create bindings.710* Sets the first argument of handleMockCall_ to the function name.711* @type {!Function}712* @private713*/714this.functionStubBinder_ = this.useMockedFunctionName_(this.handleMockCall_);715716this.mockedItem = this.useMockedFunctionName_(this.executeStub);717this.mockedItem.$stubBinder = this.functionStubBinder_;718719/**720* The call verifier is used to verify function invocations.721* Sets the first argument of verifyInvocation to the function name.722* @type {!Function}723*/724this.mockedItem.$callVerifier =725this.useMockedFunctionName_(this.verifyInvocation);726727// This has to be repeated because if it's set in base class it will be728// stubbed by MockObjectManager.729this.mockedItem.$verificationModeSetter =730goog.bind(this.setVerificationMode_, this);731};732goog.inherits(goog.labs.mock.MockFunctionManager_, goog.labs.mock.MockManager_);733734735/**736* Given a method, returns a new function that calls the first one setting737* the first argument to the mocked function name.738* This is used to dynamically override the stub binders and call verifiers.739* @private740* @param {Function} nextFunc The function to override.741* @return {!Function} The overloaded function.742*/743goog.labs.mock.MockFunctionManager_.prototype.useMockedFunctionName_ = function(744nextFunc) {745var mockFunctionManager = this;746// Avoid using 'this' because this function may be called with 'new'.747return function(var_args) {748var args = goog.array.clone(arguments);749var name = '#mockFor<' +750goog.labs.mock.getFunctionName_(mockFunctionManager.func_) + '>';751goog.array.insertAt(args, name, 0);752return nextFunc.apply(mockFunctionManager, args);753};754};755756757/**758* A stub binder is an object that helps define the stub by binding759* method name to the stub method.760* @interface761*/762goog.labs.mock.StubBinder = function() {};763764765/**766* Defines the function to be called for the method name and arguments bound767* to this {@code StubBinder}.768*769* If {@code then} or {@code thenReturn} has been previously called770* on this {@code StubBinder} then the given stub {@code func} will be called771* only after the stubs passed previously have been called. Afterwards,772* if no other calls are made to {@code then} or {@code thenReturn} for this773* {@code StubBinder} then the given {@code func} will be used for every further774* invocation.775* See #when for complete examples.776* TODO(user): Add support for the 'Answer' interface.777*778* @param {!Function} func The function to call.779* @return {!goog.labs.mock.StubBinder} Returns itself for chaining.780*/781goog.labs.mock.StubBinder.prototype.then = goog.abstractMethod;782783784/**785* Defines the constant return value for the stub represented by this786* {@code StubBinder}.787*788* @param {*} value The value to return.789* @return {!goog.labs.mock.StubBinder} Returns itself for chaining.790*/791goog.labs.mock.StubBinder.prototype.thenReturn = goog.abstractMethod;792793794/**795* A {@code StubBinder} which uses {@code MockManager_} to manage stub796* bindings.797*798* @param {!goog.labs.mock.MockManager_}799* mockManager The mock manager.800* @param {?string} name The method name.801* @param {!Array<?>} args The other arguments to the method.802*803* @implements {goog.labs.mock.StubBinder}804* @private @constructor @struct @final805*/806goog.labs.mock.StubBinderImpl_ = function(mockManager, name, args) {807/**808* The mock manager instance.809* @type {!goog.labs.mock.MockManager_}810* @private811*/812this.mockManager_ = mockManager;813814/**815* Holds the name of the method to be bound.816* @type {?string}817* @private818*/819this.name_ = name;820821/**822* Holds the arguments for the method.823* @type {!Array<?>}824* @private825*/826this.args_ = args;827828/**829* Stores a reference to the list of stubs to allow chaining sequential830* stubs.831* @private {!Array<?>}832*/833this.sequentialStubsArray_ = [];834};835836837/**838* @override839*/840goog.labs.mock.StubBinderImpl_.prototype.then = function(func) {841if (this.sequentialStubsArray_.length) {842this.sequentialStubsArray_.push(843new goog.labs.mock.MethodBinding_(this.name_, this.args_, func));844} else {845this.sequentialStubsArray_ =846this.mockManager_.addBinding(this.name_, this.args_, func);847}848return this;849};850851852/**853* @override854*/855goog.labs.mock.StubBinderImpl_.prototype.thenReturn = function(value) {856return this.then(goog.functions.constant(value));857};858859860/**861* Facilitates (and is the first step in) setting up stubs. Obtains an object862* on which, the method to be mocked is called to create a stub. Sample usage:863*864* var mockObj = goog.labs.mock.mock(objectBeingMocked);865* goog.labs.mock.when(mockObj).getFoo(3).thenReturn(4);866*867* Subsequent calls to {@code when} take precedence over earlier calls, allowing868* users to set up default stubs in setUp methods and then override them in869* individual tests.870*871* If a user wants sequential calls to their stub to return different872* values, they can chain calls to {@code then} or {@code thenReturn} as873* follows:874*875* var mockObj = goog.labs.mock.mock(objectBeingMocked);876* goog.labs.mock.when(mockObj).getFoo(3)877* .thenReturn(4)878* .then(function() {879* throw Error('exceptional case');880* });881*882* @param {!Object} mockObject The mocked object.883* @return {!goog.labs.mock.StubBinder} The property binder.884*/885goog.labs.mock.when = function(mockObject) {886goog.asserts.assert(mockObject.$stubBinder, 'Stub binder cannot be null!');887return mockObject.$stubBinder;888};889890891892/**893* Represents a binding between a method name, args and a stub.894*895* @param {?string} methodName The name of the method being stubbed.896* @param {!Array<?>} args The arguments passed to the method.897* @param {!Function} stub The stub function to be called for the given method.898* @constructor899* @struct900* @private901*/902goog.labs.mock.MethodBinding_ = function(methodName, args, stub) {903/**904* The name of the method being stubbed.905* @type {?string}906* @private907*/908this.methodName_ = methodName;909910/**911* The arguments for the method being stubbed.912* @type {!Array<?>}913* @private914*/915this.args_ = args;916917/**918* The stub function.919* @type {!Function}920* @private921*/922this.stub_ = stub;923};924925926/**927* @return {!Function} The stub to be executed.928*/929goog.labs.mock.MethodBinding_.prototype.getStub = function() {930return this.stub_;931};932933934/**935* @override936* @return {string} A readable string representation of the binding937* as a method call.938*/939goog.labs.mock.MethodBinding_.prototype.toString = function() {940return goog.labs.mock.formatMethodCall_(this.methodName_ || '', this.args_);941};942943944/**945* @return {string} The method name for this binding.946*/947goog.labs.mock.MethodBinding_.prototype.getMethodName = function() {948return this.methodName_ || '';949};950951952/**953* Determines whether the given args match the stored args_. Used to determine954* which stub to invoke for a method.955*956* @param {string} methodName The name of the method being stubbed.957* @param {!Array<?>} args An array of arguments.958* @param {boolean} isVerification Whether this is a function verification call959* or not.960* @return {boolean} If it matches the stored arguments.961*/962goog.labs.mock.MethodBinding_.prototype.matches = function(963methodName, args, isVerification) {964var specs = isVerification ? args : this.args_;965var calls = isVerification ? this.args_ : args;966967// TODO(user): More elaborate argument matching. Think about matching968// objects.969return this.methodName_ == methodName &&970goog.array.equals(calls, specs, function(arg, spec) {971// Duck-type to see if this is an object that implements the972// goog.labs.testing.Matcher interface.973if (spec && goog.isFunction(spec.matches)) {974return spec.matches(arg);975} else {976return goog.array.defaultCompareEquality(spec, arg);977}978});979};980981982