blob: a3d4483cda46907dff9595df4ebdc07651ecf2f1 [file] [log] [blame]
// 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.LooseMockTest');
goog.setTestOnly('goog.testing.LooseMockTest');
goog.require('goog.testing.LooseMock');
goog.require('goog.testing.PropertyReplacer');
goog.require('goog.testing.jsunit');
goog.require('goog.testing.mockmatchers');
// The object that we will be mocking
var RealObject = function() {
};
RealObject.prototype.a = function() {
fail('real object should never be called');
};
RealObject.prototype.b = function() {
fail('real object should never be called');
};
var mock;
var stubs;
function setUp() {
var obj = new RealObject();
mock = new goog.testing.LooseMock(obj);
stubs = new goog.testing.PropertyReplacer();
}
function tearDown() {
stubs.reset();
}
/*
* Calling this method evades the logTestFailure in
* goog.testing.Mock.prototype.$recordAndThrow, so it doesn't look like the
* test failed.
*/
function silenceFailureLogging() {
if (goog.global['G_testRunner']) {
stubs.set(goog.global['G_testRunner'],
'logTestFailure', goog.nullFunction);
}
}
function unsilenceFailureLogging() {
stubs.reset();
}
/**
* Version of assertThrows that doesn't log the exception generated by the
* mocks under test.
* TODO: would be nice to check that a particular substring is in the thrown
* message so we know it's not something dumb like a syntax error
*/
function assertThrowsQuiet(var_args) {
silenceFailureLogging();
assertThrows.apply(null, arguments);
unsilenceFailureLogging();
}
// Most of the basic functionality is tested in strictmock_test; these tests
// cover the cases where loose mocks are different from strict mocks
function testSimpleExpectations() {
mock.a(5);
mock.b();
mock.$replay();
mock.a(5);
mock.b();
mock.$verify();
mock.$reset();
mock.a();
mock.b();
mock.$replay();
mock.b();
mock.a();
mock.$verify();
mock.$reset();
mock.a(5).$times(2);
mock.a(5);
mock.a(2);
mock.$replay();
mock.a(5);
mock.a(5);
mock.a(5);
mock.a(2);
mock.$verify();
}
function testMultipleExpectations() {
mock.a().$returns(1);
mock.a().$returns(2);
mock.$replay();
assertEquals(1, mock.a());
assertEquals(2, mock.a());
mock.$verify();
}
function testMultipleExpectationArgs() {
mock.a('asdf').$anyTimes();
mock.a('qwer').$anyTimes();
mock.b().$times(3);
mock.$replay();
mock.a('asdf');
mock.b();
mock.a('asdf');
mock.a('qwer');
mock.b();
mock.a('qwer');
mock.b();
mock.$verify();
mock.$reset();
mock.a('asdf').$anyTimes();
mock.a('qwer').$anyTimes();
mock.$replay();
mock.a('asdf');
mock.a('qwer');
goog.bind(mock.a, mock, 'asdf');
goog.bind(mock.$verify, mock);
}
function testSameMethodOutOfOrder() {
mock.a('foo').$returns(1);
mock.a('bar').$returns(2);
mock.$replay();
assertEquals(2, mock.a('bar'));
assertEquals(1, mock.a('foo'));
}
function testSameMethodDifferentReturnValues() {
mock.a('foo').$returns(1).$times(2);
mock.a('foo').$returns(3);
mock.a('bar').$returns(2);
mock.$replay();
assertEquals(1, mock.a('foo'));
assertEquals(2, mock.a('bar'));
assertEquals(1, mock.a('foo'));
assertEquals(3, mock.a('foo'));
assertThrowsQuiet(function() {
mock.a('foo');
mock.$verify();
});
}
function testSameMethodBrokenExpectations() {
// This is a weird corner case.
// No way to ever make this verify no matter what you call after replaying,
// because the second expectation of mock.a('foo') will be masked by
// the first expectation that can be called any number of times, and so we
// can never satisfy that second expectation.
mock.a('foo').$returns(1).$anyTimes();
mock.a('bar').$returns(2);
mock.a('foo').$returns(3);
// LooseMock can detect this case and fail on $replay.
assertThrowsQuiet(goog.bind(mock.$replay, mock));
mock.$reset();
// This is a variant of the corner case above, but it's harder to determine
// that the expectation to mock.a('bar') can never be satisfied. So we don't
// fail on $replay, but we do fail on $verify.
mock.a(goog.testing.mockmatchers.isString).$returns(1).$anyTimes();
mock.a('bar').$returns(2);
mock.$replay();
assertEquals(1, mock.a('foo'));
assertEquals(1, mock.a('bar'));
assertThrowsQuiet(goog.bind(mock.$verify, mock));
}
function testSameMethodMultipleAnyTimes() {
mock.a('foo').$returns(1).$anyTimes();
mock.a('foo').$returns(2).$anyTimes();
mock.$replay();
assertEquals(1, mock.a('foo'));
assertEquals(1, mock.a('foo'));
assertEquals(1, mock.a('foo'));
// Note we'll never return 2 but that's ok.
mock.$verify();
}
function testFailingFast() {
mock.a().$anyTimes();
mock.$replay();
mock.a();
mock.a();
assertThrowsQuiet(goog.bind(mock.b, mock));
mock.$reset();
// too many
mock.a();
mock.b();
mock.$replay();
mock.a();
mock.b();
var message;
silenceFailureLogging();
try {
mock.a();
} catch (e) {
message = e.message;
}
unsilenceFailureLogging();
assertTrue('No exception thrown on unexpected call', goog.isDef(message));
assertContains('Too many calls to a', message);
}
function testTimes() {
mock.a().$times(3);
mock.b().$times(2);
mock.$replay();
mock.a();
mock.b();
mock.b();
mock.a();
mock.a();
mock.$verify();
}
function testFailingSlow() {
// not enough
mock.a().$times(3);
mock.$replay();
mock.a();
mock.a();
assertThrowsQuiet(goog.bind(mock.$verify, mock));
mock.$reset();
// not enough, interleaved order
mock.a().$times(3);
mock.b().$times(3);
mock.$replay();
mock.a();
mock.b();
mock.a();
mock.b();
assertThrowsQuiet(goog.bind(mock.$verify, mock));
mock.$reset();
// bad args
mock.a('asdf').$anyTimes();
mock.$replay();
mock.a('asdf');
assertThrowsQuiet(goog.bind(mock.a, mock, 'qwert'));
assertThrowsQuiet(goog.bind(mock.$verify, mock));
}
function testArgsAndReturns() {
mock.a('asdf').$atLeastOnce().$returns(5);
mock.b('qwer').$times(2).$returns(3);
mock.$replay();
assertEquals(5, mock.a('asdf'));
assertEquals(3, mock.b('qwer'));
assertEquals(5, mock.a('asdf'));
assertEquals(5, mock.a('asdf'));
assertEquals(3, mock.b('qwer'));
mock.$verify();
}
function testThrows() {
mock.a().$throws('exception!');
mock.$replay();
assertThrowsQuiet(goog.bind(mock.a, mock));
mock.$verify();
}
function testDoes() {
mock.a(1, 2).$does(function(a, b) {return a + b;});
mock.$replay();
assertEquals('Mock should call the function', 3, mock.a(1, 2));
mock.$verify();
}
function testIgnoresExtraCalls() {
mock = new goog.testing.LooseMock(RealObject, true);
mock.a();
mock.$replay();
mock.a();
mock.b(); // doesn't throw
mock.$verify();
}
function testSkipAnyTimes() {
mock = new goog.testing.LooseMock(RealObject);
mock.a(1).$anyTimes();
mock.a(2).$anyTimes();
mock.a(3).$anyTimes();
mock.$replay();
mock.a(1);
mock.a(3);
mock.$verify();
}
function testErrorMessageForBadArgs() {
mock.a();
mock.$anyTimes();
mock.$replay();
var message;
silenceFailureLogging();
try {
mock.a('a');
} catch (e) {
message = e.message;
}
unsilenceFailureLogging();
assertTrue('No exception thrown on verify', goog.isDef(message));
assertContains('Bad arguments to a()', message);
}