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 This file defines a strict mock implementation. |
17 | */ |
18 | |
19 | goog.provide('goog.testing.StrictMock'); |
20 | |
21 | goog.require('goog.array'); |
22 | goog.require('goog.testing.Mock'); |
23 | |
24 | |
25 | |
26 | /** |
27 | * This is a mock that verifies that methods are called in the order that they |
28 | * are specified during the recording phase. Since it verifies order, it |
29 | * follows 'fail fast' semantics. If it detects a deviation from the |
30 | * expectations, it will throw an exception and not wait for verify to be |
31 | * called. |
32 | * @param {Object|Function} objectToMock The object that should be mocked, or |
33 | * the constructor of an object to mock. |
34 | * @param {boolean=} opt_mockStaticMethods An optional argument denoting that |
35 | * a mock should be constructed from the static functions of a class. |
36 | * @param {boolean=} opt_createProxy An optional argument denoting that |
37 | * a proxy for the target mock should be created. |
38 | * @constructor |
39 | * @extends {goog.testing.Mock} |
40 | * @final |
41 | */ |
42 | goog.testing.StrictMock = function(objectToMock, opt_mockStaticMethods, |
43 | opt_createProxy) { |
44 | goog.testing.Mock.call(this, objectToMock, opt_mockStaticMethods, |
45 | opt_createProxy); |
46 | |
47 | /** |
48 | * An array of MockExpectations. |
49 | * @type {Array.<goog.testing.MockExpectation>} |
50 | * @private |
51 | */ |
52 | this.$expectations_ = []; |
53 | }; |
54 | goog.inherits(goog.testing.StrictMock, goog.testing.Mock); |
55 | |
56 | |
57 | /** @override */ |
58 | goog.testing.StrictMock.prototype.$recordExpectation = function() { |
59 | this.$expectations_.push(this.$pendingExpectation); |
60 | }; |
61 | |
62 | |
63 | /** @override */ |
64 | goog.testing.StrictMock.prototype.$recordCall = function(name, args) { |
65 | if (this.$expectations_.length == 0) { |
66 | this.$throwCallException(name, args); |
67 | } |
68 | |
69 | // If the current expectation has a different name, make sure it was called |
70 | // enough and then discard it. We're through with it. |
71 | var currentExpectation = this.$expectations_[0]; |
72 | while (!this.$verifyCall(currentExpectation, name, args)) { |
73 | |
74 | // This might be an item which has passed its min, and we can now |
75 | // look past it, or it might be below its min and generate an error. |
76 | if (currentExpectation.actualCalls < currentExpectation.minCalls) { |
77 | this.$throwCallException(name, args, currentExpectation); |
78 | } |
79 | |
80 | this.$expectations_.shift(); |
81 | if (this.$expectations_.length < 1) { |
82 | // Nothing left, but this may be a failed attempt to call the previous |
83 | // item on the list, which may have been between its min and max. |
84 | this.$throwCallException(name, args, currentExpectation); |
85 | } |
86 | currentExpectation = this.$expectations_[0]; |
87 | } |
88 | |
89 | if (currentExpectation.maxCalls == 0) { |
90 | this.$throwCallException(name, args); |
91 | } |
92 | |
93 | currentExpectation.actualCalls++; |
94 | // If we hit the max number of calls for this expectation, we're finished |
95 | // with it. |
96 | if (currentExpectation.actualCalls == currentExpectation.maxCalls) { |
97 | this.$expectations_.shift(); |
98 | } |
99 | |
100 | return this.$do(currentExpectation, args); |
101 | }; |
102 | |
103 | |
104 | /** @override */ |
105 | goog.testing.StrictMock.prototype.$reset = function() { |
106 | goog.testing.StrictMock.superClass_.$reset.call(this); |
107 | |
108 | goog.array.clear(this.$expectations_); |
109 | }; |
110 | |
111 | |
112 | /** @override */ |
113 | goog.testing.StrictMock.prototype.$verify = function() { |
114 | goog.testing.StrictMock.superClass_.$verify.call(this); |
115 | |
116 | while (this.$expectations_.length > 0) { |
117 | var expectation = this.$expectations_[0]; |
118 | if (expectation.actualCalls < expectation.minCalls) { |
119 | this.$throwException('Missing a call to ' + expectation.name + |
120 | '\nExpected: ' + expectation.minCalls + ' but was: ' + |
121 | expectation.actualCalls); |
122 | |
123 | } else { |
124 | // Don't need to check max, that's handled when the call is made |
125 | this.$expectations_.shift(); |
126 | } |
127 | } |
128 | }; |
129 | |
130 | |