# lib/goog/testing/strictmock.js

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 |