Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/labs/mock/mock.js
2868 views
1
// Copyright 2012 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 Provides a mocking framework in Closure to make unit tests easy
17
* to write and understand. The methods provided here can be used to replace
18
* implementations of existing objects with 'mock' objects to abstract out
19
* external services and dependencies thereby isolating the code under test.
20
* Apart from mocking, methods are also provided to just monitor calls to an
21
* object (spying) and returning specific values for some or all the inputs to
22
* methods (stubbing).
23
*
24
* Design doc : http://go/closuremock
25
*
26
*/
27
28
29
goog.provide('goog.labs.mock');
30
goog.provide('goog.labs.mock.VerificationError');
31
32
goog.require('goog.array');
33
goog.require('goog.asserts');
34
goog.require('goog.debug');
35
goog.require('goog.debug.Error');
36
goog.require('goog.functions');
37
goog.require('goog.labs.mock.verification');
38
goog.require('goog.labs.mock.verification.VerificationMode');
39
goog.require('goog.object');
40
41
goog.setTestOnly('goog.labs.mock');
42
43
44
/**
45
* Mocks a given object or class.
46
*
47
* @param {!Object} objectOrClass An instance or a constructor of a class to be
48
* mocked.
49
* @return {!Object} The mocked object.
50
*/
51
goog.labs.mock.mock = function(objectOrClass) {
52
// Go over properties of 'objectOrClass' and create a MockManager to
53
// be used for stubbing out calls to methods.
54
var mockObjectManager = new goog.labs.mock.MockObjectManager_(objectOrClass);
55
var mockedObject = mockObjectManager.getMockedItem();
56
goog.asserts.assertObject(mockedObject);
57
return /** @type {!Object} */ (mockedObject);
58
};
59
60
61
/**
62
* Mocks a given function.
63
*
64
* @param {!Function} func A function to be mocked.
65
* @return {!Function} The mocked function.
66
*/
67
goog.labs.mock.mockFunction = function(func) {
68
var mockFuncManager = new goog.labs.mock.MockFunctionManager_(func);
69
var mockedFunction = mockFuncManager.getMockedItem();
70
goog.asserts.assertFunction(mockedFunction);
71
return /** @type {!Function} */ (mockedFunction);
72
};
73
74
75
/**
76
* Mocks a given constructor.
77
*
78
* @param {!Function} ctor A constructor function to be mocked.
79
* @return {!Function} The mocked constructor.
80
*/
81
goog.labs.mock.mockConstructor = function(ctor) {
82
var mockCtor = goog.labs.mock.mockFunction(ctor);
83
84
// Copy class members from the real constructor to the mock. Do not copy
85
// the closure superClass_ property (see goog.inherits), the built-in
86
// prototype property, or properties added to Function.prototype
87
for (var property in ctor) {
88
if (property != 'superClass_' && property != 'prototype' &&
89
ctor.hasOwnProperty(property)) {
90
mockCtor[property] = ctor[property];
91
}
92
}
93
return mockCtor;
94
};
95
96
97
/**
98
* Spies on a given object.
99
*
100
* @param {!Object} obj The object to be spied on.
101
* @return {!Object} The spy object.
102
*/
103
goog.labs.mock.spy = function(obj) {
104
// Go over properties of 'obj' and create a MockSpyManager_ to
105
// be used for spying on calls to methods.
106
var mockSpyManager = new goog.labs.mock.MockSpyManager_(obj);
107
var spyObject = mockSpyManager.getMockedItem();
108
goog.asserts.assert(spyObject);
109
return spyObject;
110
};
111
112
113
/**
114
* Returns an object that can be used to verify calls to specific methods of a
115
* given mock.
116
*
117
* @param {!Object} obj The mocked object.
118
* @param {!goog.labs.mock.verification.VerificationMode=} opt_verificationMode The mode
119
* under which to verify invocations.
120
* @return {!Object} The verifier.
121
*/
122
goog.labs.mock.verify = function(obj, opt_verificationMode) {
123
var mode = opt_verificationMode || goog.labs.mock.verification.atLeast(1);
124
obj.$verificationModeSetter(mode);
125
126
return obj.$callVerifier;
127
};
128
129
130
/**
131
* Returns a name to identify a function. Named functions return their names,
132
* unnamed functions return a string of the form '#anonymous{ID}' where ID is
133
* a unique identifier for each anonymous function.
134
* @private
135
* @param {!Function} func The function.
136
* @return {string} The function name.
137
*/
138
goog.labs.mock.getFunctionName_ = function(func) {
139
var funcName = goog.debug.getFunctionName(func);
140
if (funcName == '' || funcName == '[Anonymous]') {
141
funcName = '#anonymous' + goog.labs.mock.getUid(func);
142
}
143
return funcName;
144
};
145
146
147
/**
148
* Returns a nicely formatted, readble representation of a method call.
149
* @private
150
* @param {string} methodName The name of the method.
151
* @param {Array<?>=} opt_args The method arguments.
152
* @return {string} The string representation of the method call.
153
*/
154
goog.labs.mock.formatMethodCall_ = function(methodName, opt_args) {
155
opt_args = opt_args || [];
156
opt_args = goog.array.map(opt_args, function(arg) {
157
if (goog.isFunction(arg)) {
158
var funcName = goog.labs.mock.getFunctionName_(arg);
159
return '<function ' + funcName + '>';
160
} else {
161
var isObjectWithClass = goog.isObject(arg) && !goog.isFunction(arg) &&
162
!goog.isArray(arg) && arg.constructor != Object;
163
164
if (isObjectWithClass) {
165
return arg.toString();
166
}
167
168
return goog.labs.mock.formatValue_(arg);
169
}
170
});
171
return methodName + '(' + opt_args.join(', ') + ')';
172
};
173
174
175
/**
176
* An array to store objects for unique id generation.
177
* @private
178
* @type {!Array<!Object>}
179
*/
180
goog.labs.mock.uid_ = [];
181
182
183
/**
184
* A unique Id generator that does not modify the object.
185
* @param {Object!} obj The object whose unique ID we want to generate.
186
* @return {number} an unique id for the object.
187
*/
188
goog.labs.mock.getUid = function(obj) {
189
var index = goog.array.indexOf(goog.labs.mock.uid_, obj);
190
if (index == -1) {
191
index = goog.labs.mock.uid_.length;
192
goog.labs.mock.uid_.push(obj);
193
}
194
return index;
195
};
196
197
198
/**
199
* This is just another implementation of goog.debug.deepExpose with a more
200
* compact format.
201
* @private
202
* @param {*} obj The object whose string representation will be returned.
203
* @param {boolean=} opt_id Whether to include the id of objects or not.
204
* Defaults to true.
205
* @return {string} The string representation of the object.
206
*/
207
goog.labs.mock.formatValue_ = function(obj, opt_id) {
208
var id = goog.isDef(opt_id) ? opt_id : true;
209
var previous = [];
210
var output = [];
211
212
var helper = function(obj) {
213
var indentMultiline = function(output) {
214
return output.replace(/\n/g, '\n');
215
};
216
217
218
try {
219
if (!goog.isDef(obj)) {
220
output.push('undefined');
221
} else if (goog.isNull(obj)) {
222
output.push('NULL');
223
} else if (goog.isString(obj)) {
224
output.push('"' + indentMultiline(obj) + '"');
225
} else if (goog.isFunction(obj)) {
226
var funcName = goog.labs.mock.getFunctionName_(obj);
227
output.push('<function ' + funcName + '>');
228
} else if (goog.isObject(obj)) {
229
if (goog.array.contains(previous, obj)) {
230
if (id) {
231
output.push(
232
'<recursive/dupe obj_' + goog.labs.mock.getUid(obj) + '>');
233
} else {
234
output.push('<recursive/dupe>');
235
}
236
} else {
237
previous.push(obj);
238
output.push('{');
239
for (var x in obj) {
240
output.push(' ');
241
output.push(
242
'"' + x + '"' +
243
':');
244
helper(obj[x]);
245
}
246
if (id) {
247
output.push(' _id:' + goog.labs.mock.getUid(obj));
248
}
249
output.push('}');
250
}
251
} else {
252
output.push(obj);
253
}
254
} catch (e) {
255
output.push('*** ' + e + ' ***');
256
}
257
};
258
259
helper(obj);
260
return output.join('')
261
.replace(/"closure_uid_\d+"/g, '_id')
262
.replace(/{ /g, '{');
263
264
};
265
266
267
268
/**
269
* Error thrown when verification failed.
270
*
271
* @param {Array<!goog.labs.mock.MethodBinding_>} recordedCalls
272
* The recorded calls that didn't match the expectation.
273
* @param {string} methodName The expected method call.
274
* @param {!goog.labs.mock.verification.VerificationMode} verificationMode The
275
* expected verification mode which failed verification.
276
* @param {!Array<?>} args The expected arguments.
277
* @constructor
278
* @extends {goog.debug.Error}
279
* @final
280
*/
281
goog.labs.mock.VerificationError = function(
282
recordedCalls, methodName, verificationMode, args) {
283
var msg = goog.labs.mock.VerificationError.getVerificationErrorMsg_(
284
recordedCalls, methodName, verificationMode, args);
285
goog.labs.mock.VerificationError.base(this, 'constructor', msg);
286
};
287
goog.inherits(goog.labs.mock.VerificationError, goog.debug.Error);
288
289
290
/** @override */
291
goog.labs.mock.VerificationError.prototype.name = 'VerificationError';
292
293
294
/**
295
* This array contains the name of the functions that are part of the base
296
* Object prototype.
297
* Basically a copy of goog.object.PROTOTYPE_FIELDS_.
298
* @const
299
* @type {!Array<string>}
300
* @private
301
*/
302
goog.labs.mock.PROTOTYPE_FIELDS_ = [
303
'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
304
'toLocaleString', 'toString', 'valueOf'
305
];
306
307
308
/**
309
* Constructs a descriptive error message for an expected method call.
310
* @private
311
* @param {Array<!goog.labs.mock.MethodBinding_>} recordedCalls
312
* The recorded calls that didn't match the expectation.
313
* @param {string} methodName The expected method call.
314
* @param {!goog.labs.mock.verification.VerificationMode} verificationMode The
315
* expected verification mode that failed verification.
316
* @param {!Array<?>} args The expected arguments.
317
* @return {string} The error message.
318
*/
319
goog.labs.mock.VerificationError.getVerificationErrorMsg_ = function(
320
recordedCalls, methodName, verificationMode, args) {
321
322
recordedCalls = goog.array.filter(recordedCalls, function(binding) {
323
return binding.getMethodName() == methodName;
324
});
325
326
var expected = goog.labs.mock.formatMethodCall_(methodName, args);
327
328
var msg =
329
'\nExpected: ' + expected.toString() + ' ' + verificationMode.describe();
330
msg += '\nRecorded: ';
331
332
if (recordedCalls.length > 0) {
333
msg += recordedCalls.join(',\n ');
334
} else {
335
msg += 'No recorded calls';
336
}
337
338
return msg;
339
};
340
341
342
343
/**
344
* Base class that provides basic functionality for creating, adding and
345
* finding bindings, offering an executor method that is called when a call to
346
* the stub is made, an array to hold the bindings and the mocked item, among
347
* other things.
348
*
349
* @constructor
350
* @struct
351
* @private
352
*/
353
goog.labs.mock.MockManager_ = function() {
354
/**
355
* Proxies the methods for the mocked object or class to execute the stubs.
356
* @type {!Object}
357
* @protected
358
*/
359
this.mockedItem = {};
360
361
/**
362
* A reference to the object or function being mocked.
363
* @type {Object|Function}
364
* @protected
365
*/
366
this.mockee = null;
367
368
/**
369
* Holds the stub bindings established so far.
370
* @protected
371
*/
372
this.methodBindings = [];
373
374
/**
375
* Holds a reference to the binder used to define stubs.
376
* @protected
377
*/
378
this.$stubBinder = null;
379
380
/**
381
* Record method calls with no stub definitions.
382
* @type {!Array<!goog.labs.mock.MethodBinding_>}
383
* @private
384
*/
385
this.callRecords_ = [];
386
387
/**
388
* Which {@code VerificationMode} to use during verification.
389
* @private
390
*/
391
this.verificationMode_ = goog.labs.mock.verification.atLeast(1);
392
};
393
394
395
/**
396
* Allows callers of {@code #verify} to override the default verification
397
* mode of this MockManager.
398
*
399
* @param {!goog.labs.mock.verification.VerificationMode} verificationMode
400
* @private
401
*/
402
goog.labs.mock.MockManager_.prototype.setVerificationMode_ = function(
403
verificationMode) {
404
this.verificationMode_ = verificationMode;
405
};
406
407
408
/**
409
* Handles the first step in creating a stub, returning a stub-binder that
410
* is later used to bind a stub for a method.
411
*
412
* @param {string} methodName The name of the method being bound.
413
* @param {...*} var_args The arguments to the method.
414
* @return {!goog.labs.mock.StubBinder} The stub binder.
415
* @private
416
*/
417
goog.labs.mock.MockManager_.prototype.handleMockCall_ = function(
418
methodName, var_args) {
419
var args = goog.array.slice(arguments, 1);
420
return new goog.labs.mock.StubBinderImpl_(this, methodName, args);
421
};
422
423
424
/**
425
* Returns the mock object. This should have a stubbed method for each method
426
* on the object being mocked.
427
*
428
* @return {!Object|!Function} The mock object.
429
*/
430
goog.labs.mock.MockManager_.prototype.getMockedItem = function() {
431
return this.mockedItem;
432
};
433
434
435
/**
436
* Adds a binding for the method name and arguments to be stubbed.
437
*
438
* @param {?string} methodName The name of the stubbed method.
439
* @param {!Array<?>} args The arguments passed to the method.
440
* @param {!Function} func The stub function.
441
* @return {!Array<?>} The array of stubs for further sequential stubs to be
442
* appended.
443
*/
444
goog.labs.mock.MockManager_.prototype.addBinding = function(
445
methodName, args, func) {
446
var binding = new goog.labs.mock.MethodBinding_(methodName, args, func);
447
var sequentialStubsArray = [binding];
448
goog.array.insertAt(this.methodBindings, sequentialStubsArray, 0);
449
return sequentialStubsArray;
450
};
451
452
453
/**
454
* Returns a stub, if defined, for the method name and arguments passed in.
455
* If there are multiple stubs for this method name and arguments, then
456
* the most recent binding will be used.
457
*
458
* If the next binding is a sequence of stubs, then they'll be returned
459
* in order until only one is left, at which point it will be returned for every
460
* subsequent call.
461
*
462
* @param {string} methodName The name of the stubbed method.
463
* @param {!Array<?>} args The arguments passed to the method.
464
* @return {?Function} The stub function or null.
465
* @protected
466
*/
467
goog.labs.mock.MockManager_.prototype.getNextBinding = function(
468
methodName, args) {
469
var bindings = goog.array.find(this.methodBindings, function(bindingArray) {
470
return bindingArray[0].matches(
471
methodName, args, false /* isVerification */);
472
});
473
if (bindings == null) {
474
return null;
475
}
476
477
if (bindings.length > 1) {
478
return bindings.shift().getStub();
479
}
480
return bindings[0].getStub();
481
};
482
483
484
/**
485
* Returns a stub, if defined, for the method name and arguments passed in as
486
* parameters.
487
*
488
* @param {string} methodName The name of the stubbed method.
489
* @param {!Array<?>} args The arguments passed to the method.
490
* @return {Function} The stub function or undefined.
491
* @protected
492
*/
493
goog.labs.mock.MockManager_.prototype.getExecutor = function(methodName, args) {
494
return this.getNextBinding(methodName, args);
495
};
496
497
498
/**
499
* Looks up the list of stubs defined on the mock object and executes the
500
* function associated with that stub.
501
*
502
* @param {string} methodName The name of the method to execute.
503
* @param {...*} var_args The arguments passed to the method.
504
* @return {*} Value returned by the stub function.
505
* @protected
506
*/
507
goog.labs.mock.MockManager_.prototype.executeStub = function(
508
methodName, var_args) {
509
var args = goog.array.slice(arguments, 1);
510
511
// Record this call
512
this.recordCall_(methodName, args);
513
514
var func = this.getExecutor(methodName, args);
515
if (func) {
516
return func.apply(null, args);
517
}
518
};
519
520
521
/**
522
* Records a call to 'methodName' with arguments 'args'.
523
*
524
* @param {string} methodName The name of the called method.
525
* @param {!Array<?>} args The array of arguments.
526
* @private
527
*/
528
goog.labs.mock.MockManager_.prototype.recordCall_ = function(methodName, args) {
529
var callRecord =
530
new goog.labs.mock.MethodBinding_(methodName, args, goog.nullFunction);
531
532
this.callRecords_.push(callRecord);
533
};
534
535
536
/**
537
* Verify invocation of a method with specific arguments.
538
*
539
* @param {string} methodName The name of the method.
540
* @param {...*} var_args The arguments passed.
541
* @protected
542
*/
543
goog.labs.mock.MockManager_.prototype.verifyInvocation = function(
544
methodName, var_args) {
545
var args = goog.array.slice(arguments, 1);
546
var count = goog.array.count(this.callRecords_, function(binding) {
547
return binding.matches(methodName, args, true /* isVerification */);
548
});
549
550
if (!this.verificationMode_.verify(count)) {
551
throw new goog.labs.mock.VerificationError(
552
this.callRecords_, methodName, this.verificationMode_, args);
553
}
554
};
555
556
557
558
/**
559
* Sets up mock for the given object (or class), stubbing out all the defined
560
* methods. By default, all stubs return {@code undefined}, though stubs can be
561
* later defined using {@code goog.labs.mock.when}.
562
*
563
* @param {!Object|!Function} objOrClass The object or class to set up the mock
564
* for. A class is a constructor function.
565
*
566
* @constructor
567
* @struct
568
* @extends {goog.labs.mock.MockManager_}
569
* @private
570
*/
571
goog.labs.mock.MockObjectManager_ = function(objOrClass) {
572
goog.labs.mock.MockObjectManager_.base(this, 'constructor');
573
574
/**
575
* Proxies the calls to establish the first step of the stub bindings (object
576
* and method name)
577
* @private
578
*/
579
this.objectStubBinder_ = {};
580
581
this.mockee = objOrClass;
582
583
/**
584
* The call verifier is used to verify the calls. It maps property names to
585
* the method that does call verification.
586
* @type {!Object<string, function(string, ...)>}
587
* @private
588
*/
589
this.objectCallVerifier_ = {};
590
591
var obj;
592
if (goog.isFunction(objOrClass)) {
593
// Create a temporary subclass with a no-op constructor so that we can
594
// create an instance and determine what methods it has.
595
/**
596
* @constructor
597
* @final
598
*/
599
var tempCtor = function() {};
600
goog.inherits(tempCtor, objOrClass);
601
obj = new tempCtor();
602
} else {
603
obj = objOrClass;
604
}
605
606
// Put the object being mocked in the prototype chain of the mock so that
607
// it has all the correct properties and instanceof works.
608
/**
609
* @constructor
610
* @final
611
*/
612
var mockedItemCtor = function() {};
613
mockedItemCtor.prototype = obj;
614
this.mockedItem = new mockedItemCtor();
615
616
var enumerableProperties = goog.object.getAllPropertyNames(obj);
617
// The non enumerable properties are added due to the fact that IE8 does not
618
// enumerate any of the prototype Object functions even when overriden and
619
// mocking these is sometimes needed.
620
for (var i = 0; i < goog.labs.mock.PROTOTYPE_FIELDS_.length; i++) {
621
var prop = goog.labs.mock.PROTOTYPE_FIELDS_[i];
622
if (!goog.array.contains(enumerableProperties, prop)) {
623
enumerableProperties.push(prop);
624
}
625
}
626
627
// Adds the properties to the mock, creating a proxy stub for each method on
628
// the instance.
629
for (var i = 0; i < enumerableProperties.length; i++) {
630
var prop = enumerableProperties[i];
631
if (goog.isFunction(obj[prop])) {
632
this.mockedItem[prop] = goog.bind(this.executeStub, this, prop);
633
// The stub binder used to create bindings.
634
this.objectStubBinder_[prop] =
635
goog.bind(this.handleMockCall_, this, prop);
636
// The verifier verifies the calls.
637
this.objectCallVerifier_[prop] =
638
goog.bind(this.verifyInvocation, this, prop);
639
}
640
}
641
// The alias for stub binder exposed to the world.
642
this.mockedItem.$stubBinder = this.objectStubBinder_;
643
644
// The alias for verifier for the world.
645
this.mockedItem.$callVerifier = this.objectCallVerifier_;
646
647
this.mockedItem.$verificationModeSetter =
648
goog.bind(this.setVerificationMode_, this);
649
};
650
goog.inherits(goog.labs.mock.MockObjectManager_, goog.labs.mock.MockManager_);
651
652
653
654
/**
655
* Sets up the spying behavior for the given object.
656
*
657
* @param {!Object} obj The object to be spied on.
658
*
659
* @constructor
660
* @struct
661
* @extends {goog.labs.mock.MockObjectManager_}
662
* @private
663
*/
664
goog.labs.mock.MockSpyManager_ = function(obj) {
665
goog.labs.mock.MockSpyManager_.base(this, 'constructor', obj);
666
};
667
goog.inherits(
668
goog.labs.mock.MockSpyManager_, goog.labs.mock.MockObjectManager_);
669
670
671
/**
672
* Return a stub, if defined, for the method and arguments passed in. If we lack
673
* a stub, instead look for a call record that matches the method and arguments.
674
*
675
* @return {!Function} The stub or the invocation logger, if defined.
676
* @override
677
*/
678
goog.labs.mock.MockSpyManager_.prototype.getNextBinding = function(
679
methodName, args) {
680
var stub = goog.labs.mock.MockSpyManager_.base(
681
this, 'getNextBinding', methodName, args);
682
683
if (!stub) {
684
stub = goog.bind(this.mockee[methodName], this.mockee);
685
}
686
687
return stub;
688
};
689
690
691
692
/**
693
* Sets up mock for the given function, stubbing out. By default, all stubs
694
* return {@code undefined}, though stubs can be later defined using
695
* {@code goog.labs.mock.when}.
696
*
697
* @param {!Function} func The function to set up the mock for.
698
*
699
* @constructor
700
* @struct
701
* @extends {goog.labs.mock.MockManager_}
702
* @private
703
*/
704
goog.labs.mock.MockFunctionManager_ = function(func) {
705
goog.labs.mock.MockFunctionManager_.base(this, 'constructor');
706
707
this.func_ = func;
708
709
/**
710
* The stub binder used to create bindings.
711
* Sets the first argument of handleMockCall_ to the function name.
712
* @type {!Function}
713
* @private
714
*/
715
this.functionStubBinder_ = this.useMockedFunctionName_(this.handleMockCall_);
716
717
this.mockedItem = this.useMockedFunctionName_(this.executeStub);
718
this.mockedItem.$stubBinder = this.functionStubBinder_;
719
720
/**
721
* The call verifier is used to verify function invocations.
722
* Sets the first argument of verifyInvocation to the function name.
723
* @type {!Function}
724
*/
725
this.mockedItem.$callVerifier =
726
this.useMockedFunctionName_(this.verifyInvocation);
727
728
// This has to be repeated because if it's set in base class it will be
729
// stubbed by MockObjectManager.
730
this.mockedItem.$verificationModeSetter =
731
goog.bind(this.setVerificationMode_, this);
732
};
733
goog.inherits(goog.labs.mock.MockFunctionManager_, goog.labs.mock.MockManager_);
734
735
736
/**
737
* Given a method, returns a new function that calls the first one setting
738
* the first argument to the mocked function name.
739
* This is used to dynamically override the stub binders and call verifiers.
740
* @private
741
* @param {Function} nextFunc The function to override.
742
* @return {!Function} The overloaded function.
743
*/
744
goog.labs.mock.MockFunctionManager_.prototype.useMockedFunctionName_ = function(
745
nextFunc) {
746
var mockFunctionManager = this;
747
// Avoid using 'this' because this function may be called with 'new'.
748
return function(var_args) {
749
var args = goog.array.clone(arguments);
750
var name = '#mockFor<' +
751
goog.labs.mock.getFunctionName_(mockFunctionManager.func_) + '>';
752
goog.array.insertAt(args, name, 0);
753
return nextFunc.apply(mockFunctionManager, args);
754
};
755
};
756
757
758
/**
759
* A stub binder is an object that helps define the stub by binding
760
* method name to the stub method.
761
* @interface
762
*/
763
goog.labs.mock.StubBinder = function() {};
764
765
766
/**
767
* Defines the function to be called for the method name and arguments bound
768
* to this {@code StubBinder}.
769
*
770
* If {@code then} or {@code thenReturn} has been previously called
771
* on this {@code StubBinder} then the given stub {@code func} will be called
772
* only after the stubs passed previously have been called. Afterwards,
773
* if no other calls are made to {@code then} or {@code thenReturn} for this
774
* {@code StubBinder} then the given {@code func} will be used for every further
775
* invocation.
776
* See #when for complete examples.
777
* TODO(user): Add support for the 'Answer' interface.
778
*
779
* @param {!Function} func The function to call.
780
* @return {!goog.labs.mock.StubBinder} Returns itself for chaining.
781
*/
782
goog.labs.mock.StubBinder.prototype.then = goog.abstractMethod;
783
784
785
/**
786
* Defines the constant return value for the stub represented by this
787
* {@code StubBinder}.
788
*
789
* @param {*} value The value to return.
790
* @return {!goog.labs.mock.StubBinder} Returns itself for chaining.
791
*/
792
goog.labs.mock.StubBinder.prototype.thenReturn = goog.abstractMethod;
793
794
795
/**
796
* A {@code StubBinder} which uses {@code MockManager_} to manage stub
797
* bindings.
798
*
799
* @param {!goog.labs.mock.MockManager_}
800
* mockManager The mock manager.
801
* @param {?string} name The method name.
802
* @param {!Array<?>} args The other arguments to the method.
803
*
804
* @implements {goog.labs.mock.StubBinder}
805
* @private @constructor @struct @final
806
*/
807
goog.labs.mock.StubBinderImpl_ = function(mockManager, name, args) {
808
/**
809
* The mock manager instance.
810
* @type {!goog.labs.mock.MockManager_}
811
* @private
812
*/
813
this.mockManager_ = mockManager;
814
815
/**
816
* Holds the name of the method to be bound.
817
* @type {?string}
818
* @private
819
*/
820
this.name_ = name;
821
822
/**
823
* Holds the arguments for the method.
824
* @type {!Array<?>}
825
* @private
826
*/
827
this.args_ = args;
828
829
/**
830
* Stores a reference to the list of stubs to allow chaining sequential
831
* stubs.
832
* @private {!Array<?>}
833
*/
834
this.sequentialStubsArray_ = [];
835
};
836
837
838
/**
839
* @override
840
*/
841
goog.labs.mock.StubBinderImpl_.prototype.then = function(func) {
842
if (this.sequentialStubsArray_.length) {
843
this.sequentialStubsArray_.push(
844
new goog.labs.mock.MethodBinding_(this.name_, this.args_, func));
845
} else {
846
this.sequentialStubsArray_ =
847
this.mockManager_.addBinding(this.name_, this.args_, func);
848
}
849
return this;
850
};
851
852
853
/**
854
* @override
855
*/
856
goog.labs.mock.StubBinderImpl_.prototype.thenReturn = function(value) {
857
return this.then(goog.functions.constant(value));
858
};
859
860
861
/**
862
* Facilitates (and is the first step in) setting up stubs. Obtains an object
863
* on which, the method to be mocked is called to create a stub. Sample usage:
864
*
865
* var mockObj = goog.labs.mock.mock(objectBeingMocked);
866
* goog.labs.mock.when(mockObj).getFoo(3).thenReturn(4);
867
*
868
* Subsequent calls to {@code when} take precedence over earlier calls, allowing
869
* users to set up default stubs in setUp methods and then override them in
870
* individual tests.
871
*
872
* If a user wants sequential calls to their stub to return different
873
* values, they can chain calls to {@code then} or {@code thenReturn} as
874
* follows:
875
*
876
* var mockObj = goog.labs.mock.mock(objectBeingMocked);
877
* goog.labs.mock.when(mockObj).getFoo(3)
878
* .thenReturn(4)
879
* .then(function() {
880
* throw Error('exceptional case');
881
* });
882
*
883
* @param {!Object} mockObject The mocked object.
884
* @return {!goog.labs.mock.StubBinder} The property binder.
885
*/
886
goog.labs.mock.when = function(mockObject) {
887
goog.asserts.assert(mockObject.$stubBinder, 'Stub binder cannot be null!');
888
return mockObject.$stubBinder;
889
};
890
891
892
893
/**
894
* Represents a binding between a method name, args and a stub.
895
*
896
* @param {?string} methodName The name of the method being stubbed.
897
* @param {!Array<?>} args The arguments passed to the method.
898
* @param {!Function} stub The stub function to be called for the given method.
899
* @constructor
900
* @struct
901
* @private
902
*/
903
goog.labs.mock.MethodBinding_ = function(methodName, args, stub) {
904
/**
905
* The name of the method being stubbed.
906
* @type {?string}
907
* @private
908
*/
909
this.methodName_ = methodName;
910
911
/**
912
* The arguments for the method being stubbed.
913
* @type {!Array<?>}
914
* @private
915
*/
916
this.args_ = args;
917
918
/**
919
* The stub function.
920
* @type {!Function}
921
* @private
922
*/
923
this.stub_ = stub;
924
};
925
926
927
/**
928
* @return {!Function} The stub to be executed.
929
*/
930
goog.labs.mock.MethodBinding_.prototype.getStub = function() {
931
return this.stub_;
932
};
933
934
935
/**
936
* @override
937
* @return {string} A readable string representation of the binding
938
* as a method call.
939
*/
940
goog.labs.mock.MethodBinding_.prototype.toString = function() {
941
return goog.labs.mock.formatMethodCall_(this.methodName_ || '', this.args_);
942
};
943
944
945
/**
946
* @return {string} The method name for this binding.
947
*/
948
goog.labs.mock.MethodBinding_.prototype.getMethodName = function() {
949
return this.methodName_ || '';
950
};
951
952
953
/**
954
* Determines whether the given args match the stored args_. Used to determine
955
* which stub to invoke for a method.
956
*
957
* @param {string} methodName The name of the method being stubbed.
958
* @param {!Array<?>} args An array of arguments.
959
* @param {boolean} isVerification Whether this is a function verification call
960
* or not.
961
* @return {boolean} If it matches the stored arguments.
962
*/
963
goog.labs.mock.MethodBinding_.prototype.matches = function(
964
methodName, args, isVerification) {
965
var specs = isVerification ? args : this.args_;
966
var calls = isVerification ? this.args_ : args;
967
968
// TODO(user): More elaborate argument matching. Think about matching
969
// objects.
970
return this.methodName_ == methodName &&
971
goog.array.equals(calls, specs, function(arg, spec) {
972
// Duck-type to see if this is an object that implements the
973
// goog.labs.testing.Matcher interface.
974
if (spec && goog.isFunction(spec.matches)) {
975
return spec.matches(arg);
976
} else {
977
return goog.array.defaultCompareEquality(spec, arg);
978
}
979
});
980
};
981
982