| // Copyright 2008 The Closure Library Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS-IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| goog.provide('goog.testing.FunctionMockTest'); |
| goog.setTestOnly('goog.testing.FunctionMockTest'); |
| |
| goog.require('goog.array'); |
| goog.require('goog.string'); |
| goog.require('goog.testing'); |
| goog.require('goog.testing.FunctionMock'); |
| goog.require('goog.testing.Mock'); |
| goog.require('goog.testing.StrictMock'); |
| goog.require('goog.testing.asserts'); |
| goog.require('goog.testing.jsunit'); |
| goog.require('goog.testing.mockmatchers'); |
| |
| // Global scope so we can tear it down safely |
| var mockGlobal; |
| |
| function tearDown() { |
| if (mockGlobal) { |
| mockGlobal.$tearDown(); |
| } |
| } |
| |
| |
| //----- Tests for goog.testing.FunctionMock |
| |
| function testMockFunctionCallOrdering() { |
| var doOneTest = function(mockFunction, success, expected_args, actual_args) { |
| goog.array.forEach(expected_args, function(arg) { mockFunction(arg); }); |
| mockFunction.$replay(); |
| var callFunction = function() { |
| goog.array.forEach(actual_args, function(arg) { mockFunction(arg); }); |
| mockFunction.$verify(); |
| }; |
| if (success) { |
| callFunction(); |
| } else { |
| assertThrows(callFunction); |
| } |
| }; |
| |
| var doTest = function(strict_ok, loose_ok, expected_args, actual_args) { |
| doOneTest(goog.testing.createFunctionMock(), strict_ok, |
| expected_args, actual_args); |
| doOneTest(goog.testing.createFunctionMock('name'), strict_ok, |
| expected_args, actual_args); |
| doOneTest(goog.testing.createFunctionMock('name', goog.testing.Mock.STRICT), |
| strict_ok, expected_args, actual_args); |
| doOneTest(goog.testing.createFunctionMock('name', goog.testing.Mock.LOOSE), |
| loose_ok, expected_args, actual_args); |
| }; |
| |
| doTest(true, true, [1, 2], [1, 2]); |
| doTest(false, true, [1, 2], [2, 1]); |
| doTest(false, false, [1, 2], [2, 2]); |
| doTest(false, false, [1, 2], [1]); |
| doTest(false, false, [1, 2], [1, 1]); |
| doTest(false, false, [1, 2], [1]); |
| } |
| |
| function testMocksFunctionWithNoArgs() { |
| var mockFoo = goog.testing.createFunctionMock(); |
| mockFoo(); |
| mockFoo.$replay(); |
| mockFoo(); |
| mockFoo.$verify(); |
| } |
| |
| function testMocksFunctionWithOneArg() { |
| var mockFoo = goog.testing.createFunctionMock(); |
| mockFoo('x'); |
| mockFoo.$replay(); |
| mockFoo('x'); |
| mockFoo.$verify(); |
| } |
| |
| function testMocksFunctionWithMultipleArgs() { |
| var mockFoo = goog.testing.createFunctionMock(); |
| mockFoo('x', 'y'); |
| mockFoo.$replay(); |
| mockFoo('x', 'y'); |
| mockFoo.$verify(); |
| } |
| |
| function testFailsIfCalledWithIncorrectArgs() { |
| var mockFoo = goog.testing.createFunctionMock(); |
| |
| mockFoo(); |
| mockFoo.$replay(); |
| assertThrows(function() {mockFoo('x');}); |
| mockFoo.$reset(); |
| |
| mockFoo('x'); |
| mockFoo.$replay(); |
| assertThrows(function() {mockFoo();}); |
| mockFoo.$reset(); |
| |
| mockFoo('x'); |
| mockFoo.$replay(); |
| assertThrows(function() {mockFoo('x', 'y');}); |
| mockFoo.$reset(); |
| |
| mockFoo('x', 'y'); |
| mockFoo.$replay(); |
| assertThrows(function() {mockFoo('x');}); |
| mockFoo.$reset(); |
| |
| mockFoo('correct'); |
| mockFoo.$replay(); |
| assertThrows(function() {mockFoo('wrong');}); |
| mockFoo.$reset(); |
| |
| mockFoo('correct', 'args'); |
| mockFoo.$replay(); |
| assertThrows(function() {mockFoo('wrong', 'args');}); |
| mockFoo.$reset(); |
| } |
| |
| function testMocksFunctionWithReturnValue() { |
| var mockFoo = goog.testing.createFunctionMock(); |
| mockFoo().$returns('bar'); |
| mockFoo.$replay(); |
| assertEquals('bar', mockFoo()); |
| mockFoo.$verify(); |
| } |
| |
| function testFunctionMockWorksWhenPassedAsACallback() { |
| var invoker = { |
| register: function(callback) { |
| this.callback = callback; |
| }, |
| |
| invoke: function(args) { |
| return this.callback(args); |
| } |
| }; |
| |
| var mockFunction = goog.testing.createFunctionMock(); |
| mockFunction('bar').$returns('baz'); |
| |
| mockFunction.$replay(); |
| invoker.register(mockFunction); |
| assertEquals('baz', invoker.invoke('bar')); |
| mockFunction.$verify(); |
| } |
| |
| function testFunctionMockQuacksLikeAStrictMock() { |
| var mockFunction = goog.testing.createFunctionMock(); |
| assertQuacksLike(mockFunction, goog.testing.StrictMock); |
| } |
| |
| |
| //----- Global functions for goog.testing.GlobalFunctionMock to mock |
| |
| function globalFoo() { |
| return 'I am Spartacus!'; |
| } |
| |
| function globalBar(who, what) { |
| return [who, 'is', what].join(' '); |
| } |
| |
| |
| //----- Tests for goog.testing.createGlobalFunctionMock |
| |
| function testMocksGlobalFunctionWithNoArgs() { |
| mockGlobal = goog.testing.createGlobalFunctionMock('globalFoo'); |
| mockGlobal().$returns('No, I am Spartacus!'); |
| |
| mockGlobal.$replay(); |
| assertEquals('No, I am Spartacus!', globalFoo()); |
| mockGlobal.$verify(); |
| } |
| |
| function testMocksGlobalFunctionUsingGlobalName() { |
| goog.testing.createGlobalFunctionMock('globalFoo'); |
| globalFoo().$returns('No, I am Spartacus!'); |
| |
| globalFoo.$replay(); |
| assertEquals('No, I am Spartacus!', globalFoo()); |
| globalFoo.$verify(); |
| globalFoo.$tearDown(); |
| } |
| |
| function testMocksGlobalFunctionWithArgs() { |
| var mockReturnValue = 'Noam is Chomsky!'; |
| mockGlobal = goog.testing.createGlobalFunctionMock('globalBar'); |
| mockGlobal('Noam', 'Spartacus').$returns(mockReturnValue); |
| |
| mockGlobal.$replay(); |
| assertEquals(mockReturnValue, globalBar('Noam', 'Spartacus')); |
| mockGlobal.$verify(); |
| } |
| |
| function testGlobalFunctionMockFailsWithIncorrectArgs() { |
| mockGlobal = goog.testing.createGlobalFunctionMock('globalBar'); |
| mockGlobal('a', 'b'); |
| |
| mockGlobal.$replay(); |
| |
| assertThrows('Mock should have failed because of incorrect arguments', |
| function() {globalBar('b', 'a')}); |
| } |
| |
| function testGlobalFunctionMockQuacksLikeAFunctionMock() { |
| mockGlobal = goog.testing.createGlobalFunctionMock('globalFoo'); |
| assertQuacksLike(mockGlobal, goog.testing.FunctionMock); |
| } |
| |
| function testMockedFunctionsAvailableInGlobalAndGoogGlobalAndWindowScope() { |
| mockGlobal = goog.testing.createGlobalFunctionMock('globalFoo'); |
| |
| // we expect this call 3 times through global, goog.global and window scope |
| mockGlobal().$times(3); |
| |
| mockGlobal.$replay(); |
| goog.global.globalFoo(); |
| window.globalFoo(); |
| globalFoo(); |
| mockGlobal.$verify(); |
| } |
| |
| function testTearDownRestoresOriginalGlobalFunction() { |
| mockGlobal = goog.testing.createGlobalFunctionMock('globalFoo'); |
| mockGlobal().$returns('No, I am Spartacus!'); |
| |
| mockGlobal.$replay(); |
| assertEquals('No, I am Spartacus!', globalFoo()); |
| mockGlobal.$tearDown(); |
| assertEquals('I am Spartacus!', globalFoo()); |
| mockGlobal.$verify(); |
| } |
| |
| function testTearDownHandlesMultipleMocking() { |
| var mock1 = goog.testing.createGlobalFunctionMock('globalFoo'); |
| var mock2 = goog.testing.createGlobalFunctionMock('globalFoo'); |
| var mock3 = goog.testing.createGlobalFunctionMock('globalFoo'); |
| mock1().$returns('No, I am Spartacus 1!'); |
| mock2().$returns('No, I am Spartacus 2!'); |
| mock3().$returns('No, I am Spartacus 3!'); |
| |
| mock1.$replay(); |
| mock2.$replay(); |
| mock3.$replay(); |
| assertEquals('No, I am Spartacus 3!', globalFoo()); |
| mock3.$tearDown(); |
| assertEquals('No, I am Spartacus 2!', globalFoo()); |
| mock2.$tearDown(); |
| assertEquals('No, I am Spartacus 1!', globalFoo()); |
| mock1.$tearDown(); |
| assertEquals('I am Spartacus!', globalFoo()); |
| } |
| |
| function testGlobalFunctionMockCallOrdering() { |
| var mock = goog.testing.createGlobalFunctionMock('globalFoo'); |
| mock(1); |
| mock(2); |
| mock.$replay(); |
| assertThrows(function() {globalFoo(2);}); |
| mock.$tearDown(); |
| |
| mock = goog.testing.createGlobalFunctionMock('globalFoo', |
| goog.testing.Mock.STRICT); |
| mock(1); |
| mock(2); |
| mock.$replay(); |
| globalFoo(1); |
| globalFoo(2); |
| mock.$verify(); |
| mock.$tearDown(); |
| |
| mock = goog.testing.createGlobalFunctionMock('globalFoo', |
| goog.testing.Mock.STRICT); |
| mock(1); |
| mock(2); |
| mock.$replay(); |
| assertThrows(function() {globalFoo(2);}); |
| mock.$tearDown(); |
| |
| mock = goog.testing.createGlobalFunctionMock('globalFoo', |
| goog.testing.Mock.LOOSE); |
| mock(1); |
| mock(2); |
| mock.$replay(); |
| globalFoo(2); |
| globalFoo(1); |
| mock.$verify(); |
| mock.$tearDown(); |
| } |
| |
| //----- Functions for goog.testing.MethodMock to mock |
| |
| var mynamespace = {}; |
| |
| mynamespace.myMethod = function() { |
| return 'I should be mocked.'; |
| }; |
| |
| function testMocksMethod() { |
| mockMethod = goog.testing.createMethodMock(mynamespace, 'myMethod'); |
| mockMethod().$returns('I have been mocked!'); |
| |
| mockMethod.$replay(); |
| assertEquals('I have been mocked!', mockMethod()); |
| mockMethod.$verify(); |
| } |
| |
| function testMocksMethodInNamespace() { |
| goog.testing.createMethodMock(mynamespace, 'myMethod'); |
| mynamespace.myMethod().$returns('I have been mocked!'); |
| |
| mynamespace.myMethod.$replay(); |
| assertEquals('I have been mocked!', mynamespace.myMethod()); |
| mynamespace.myMethod.$verify(); |
| mynamespace.myMethod.$tearDown(); |
| } |
| |
| function testMethodMockCanOnlyMockExistingMethods() { |
| assertThrows(function() { |
| goog.testing.createMethodMock(mynamespace, 'doesNotExist'); |
| }); |
| } |
| |
| function testMethodMockCallOrdering() { |
| goog.testing.createMethodMock(mynamespace, 'myMethod'); |
| mynamespace.myMethod(1); |
| mynamespace.myMethod(2); |
| mynamespace.myMethod.$replay(); |
| assertThrows(function() {mynamespace.myMethod(2);}); |
| mynamespace.myMethod.$tearDown(); |
| |
| goog.testing.createMethodMock(mynamespace, 'myMethod', |
| goog.testing.Mock.STRICT); |
| mynamespace.myMethod(1); |
| mynamespace.myMethod(2); |
| mynamespace.myMethod.$replay(); |
| mynamespace.myMethod(1); |
| mynamespace.myMethod(2); |
| mynamespace.myMethod.$verify(); |
| mynamespace.myMethod.$tearDown(); |
| |
| goog.testing.createMethodMock(mynamespace, 'myMethod', |
| goog.testing.Mock.STRICT); |
| mynamespace.myMethod(1); |
| mynamespace.myMethod(2); |
| mynamespace.myMethod.$replay(); |
| assertThrows(function() {mynamespace.myMethod(2);}); |
| mynamespace.myMethod.$tearDown(); |
| |
| goog.testing.createMethodMock(mynamespace, 'myMethod', |
| goog.testing.Mock.LOOSE); |
| mynamespace.myMethod(1); |
| mynamespace.myMethod(2); |
| mynamespace.myMethod.$replay(); |
| mynamespace.myMethod(2); |
| mynamespace.myMethod(1); |
| mynamespace.myMethod.$verify(); |
| mynamespace.myMethod.$tearDown(); |
| } |
| |
| //----- Functions for goog.testing.createConstructorMock to mock |
| |
| var constructornamespace = {}; |
| |
| constructornamespace.MyConstructor = function() { |
| }; |
| |
| constructornamespace.MyConstructor.prototype.myMethod = function() { |
| return 'I should be mocked.'; |
| }; |
| |
| constructornamespace.MyConstructorWithArgument = function(argument) { |
| this.argument_ = argument; |
| }; |
| |
| constructornamespace.MyConstructorWithArgument.prototype.myMethod = function() { |
| return this.argument_; |
| }; |
| |
| constructornamespace.MyConstructorWithClassMembers = function() { |
| }; |
| |
| constructornamespace.MyConstructorWithClassMembers.CONSTANT = 42; |
| |
| constructornamespace.MyConstructorWithClassMembers.classMethod = function() { |
| return 'class method return value'; |
| }; |
| |
| function testConstructorMock() { |
| var mockObject = |
| new goog.testing.StrictMock(constructornamespace.MyConstructor); |
| var mockConstructor = goog.testing.createConstructorMock( |
| constructornamespace, 'MyConstructor'); |
| mockConstructor().$returns(mockObject); |
| mockObject.myMethod().$returns('I have been mocked!'); |
| |
| mockConstructor.$replay(); |
| mockObject.$replay(); |
| assertEquals('I have been mocked!', |
| new constructornamespace.MyConstructor().myMethod()); |
| mockConstructor.$verify(); |
| mockObject.$verify(); |
| mockConstructor.$tearDown(); |
| } |
| |
| function testConstructorMockWithArgument() { |
| var mockObject = new goog.testing.StrictMock( |
| constructornamespace.MyConstructorWithArgument); |
| var mockConstructor = goog.testing.createConstructorMock( |
| constructornamespace, 'MyConstructorWithArgument'); |
| mockConstructor(goog.testing.mockmatchers.isString).$returns(mockObject); |
| mockObject.myMethod().$returns('I have been mocked!'); |
| |
| mockConstructor.$replay(); |
| mockObject.$replay(); |
| assertEquals('I have been mocked!', |
| new constructornamespace.MyConstructorWithArgument('I should be mocked.') |
| .myMethod()); |
| mockConstructor.$verify(); |
| mockObject.$verify(); |
| mockConstructor.$tearDown(); |
| } |
| |
| |
| /** |
| * Test that class members are copied to the mock constructor. |
| */ |
| function testConstructorMockWithClassMembers() { |
| var mockConstructor = goog.testing.createConstructorMock( |
| constructornamespace, 'MyConstructorWithClassMembers'); |
| assertEquals(42, constructornamespace.MyConstructorWithClassMembers.CONSTANT); |
| assertEquals('class method return value', |
| constructornamespace.MyConstructorWithClassMembers.classMethod()); |
| mockConstructor.$tearDown(); |
| } |
| |
| function testConstructorMockCallOrdering() { |
| var instance = {}; |
| |
| goog.testing.createConstructorMock(constructornamespace, |
| 'MyConstructorWithArgument'); |
| constructornamespace.MyConstructorWithArgument(1).$returns(instance); |
| constructornamespace.MyConstructorWithArgument(2).$returns(instance); |
| constructornamespace.MyConstructorWithArgument.$replay(); |
| assertThrows( |
| function() {new constructornamespace.MyConstructorWithArgument(2);}); |
| constructornamespace.MyConstructorWithArgument.$tearDown(); |
| |
| goog.testing.createConstructorMock(constructornamespace, |
| 'MyConstructorWithArgument', |
| goog.testing.Mock.STRICT); |
| constructornamespace.MyConstructorWithArgument(1).$returns(instance); |
| constructornamespace.MyConstructorWithArgument(2).$returns(instance); |
| constructornamespace.MyConstructorWithArgument.$replay(); |
| new constructornamespace.MyConstructorWithArgument(1); |
| new constructornamespace.MyConstructorWithArgument(2); |
| constructornamespace.MyConstructorWithArgument.$verify(); |
| constructornamespace.MyConstructorWithArgument.$tearDown(); |
| |
| goog.testing.createConstructorMock(constructornamespace, |
| 'MyConstructorWithArgument', |
| goog.testing.Mock.STRICT); |
| constructornamespace.MyConstructorWithArgument(1).$returns(instance); |
| constructornamespace.MyConstructorWithArgument(2).$returns(instance); |
| constructornamespace.MyConstructorWithArgument.$replay(); |
| assertThrows( |
| function() {new constructornamespace.MyConstructorWithArgument(2);}); |
| constructornamespace.MyConstructorWithArgument.$tearDown(); |
| |
| goog.testing.createConstructorMock(constructornamespace, |
| 'MyConstructorWithArgument', |
| goog.testing.Mock.LOOSE); |
| constructornamespace.MyConstructorWithArgument(1).$returns(instance); |
| constructornamespace.MyConstructorWithArgument(2).$returns(instance); |
| constructornamespace.MyConstructorWithArgument.$replay(); |
| new constructornamespace.MyConstructorWithArgument(2); |
| new constructornamespace.MyConstructorWithArgument(1); |
| constructornamespace.MyConstructorWithArgument.$verify(); |
| constructornamespace.MyConstructorWithArgument.$tearDown(); |
| } |
| |
| //----- Helper assertions |
| |
| function assertQuacksLike(obj, target) { |
| for (meth in target.prototype) { |
| if (!goog.string.endsWith(meth, '_')) { |
| assertNotUndefined('Should have implemented ' + meth + '()', obj[meth]); |
| } |
| } |
| } |