docs/concepts/mocks/error-handling.md
Mocks have strict verification requirements and will throw errors when expectations aren't met or when used incorrectly. Understanding these errors is essential for effective mock usage.
Attempting to create a mock for a null or undefined object will fail.
// This will throw: "object is null"
sinon.mock(null);
sinon.mock(undefined);
Solution: Provide a valid object:
const obj = { method() {} };
const mock = sinon.mock(obj);
Setting an expectation on a falsy method name will fail.
const mock = sinon.mock(obj);
// This will throw: "method is falsy"
mock.expects(null);
mock.expects(undefined);
mock.expects("");
Solution: Provide a valid method name:
mock.expects("validMethodName");
The most common mock error: calling verify() when expectations aren't met.
const obj = { greet() {} };
const mock = sinon.mock(obj);
mock.expects("greet").once();
// greet is never called
mock.verify(); // Throws: "Expected greet(...) once (never called)"
Error message includes:
Solution: Ensure all expectations are met:
const obj = { greet() {} };
const mock = sinon.mock(obj);
mock.expects("greet").once();
obj.greet(); // Call the method
mock.verify(); // Now succeeds
Calling a mocked method in a way that doesn't match any expectations will fail immediately.
const obj = { greet(name) {} };
const mock = sinon.mock(obj);
mock.expects("greet").withExactArgs("Alice");
// This throws immediately: "Unexpected call: greet(Bob)"
obj.greet("Bob");
Error message includes:
Solution: Either adjust the expectation or fix the call:
// Option 1: Use withArgs instead of withExactArgs
mock.expects("greet").withArgs("Alice"); // Also accepts more args
// Option 2: Match the expectation exactly
obj.greet("Alice"); // Now matches
// Option 3: Add multiple expectations
mock.expects("greet").withExactArgs("Alice");
mock.expects("greet").withExactArgs("Bob");
An important behavior: mock.verify() automatically calls mock.restore().
const obj = {
method() {
return "original";
}
};
const mock = sinon.mock(obj);
mock.expects("method").once();
obj.method();
mock.verify(); // Verifies AND restores
// Method is now restored
obj.method(); // Returns 'original', not a mock
This is by design. The expectation is that verify is your last action with the mock.
Problem: Calling verify() twice will fail on the second call:
mock.verify(); // First call succeeds and restores
mock.verify(); // Second call throws - mock is already restored
Solution: Only call verify() once per mock, typically in test cleanup:
afterEach(() => {
mock.verify(); // Verify once at the end
});
Setting multiple expectations with withArgs() or withExactArgs() on the same expectation will overwrite previous arguments.
const mock = sinon.mock(obj);
const expectation = mock.expects("method");
expectation.withArgs("first");
expectation.withArgs("second"); // Overwrites 'first'
obj.method("first"); // Fails! Expectation now requires 'second'
This is documented behavior: An expectation holds only one set of arguments.
Solution: Create multiple expectations for different arguments:
const mock = sinon.mock(obj);
mock.expects("method").withArgs("first");
mock.expects("method").withArgs("second");
obj.method("first"); // Matches first expectation
obj.method("second"); // Matches second expectation
Calling a method more times than expected will fail.
const mock = sinon.mock(obj);
mock.expects("method").once();
obj.method(); // OK
obj.method(); // Throws: "Unexpected call: method()"
Solution: Adjust expectation or use atLeast():
// Option 1: Set correct count
mock.expects("method").twice();
// Option 2: Use atLeast for minimum
mock.expects("method").atLeast(1); // Allows 1 or more
// Option 3: Use atMost for maximum
mock.expects("method").atMost(2); // Allows 0, 1, or 2
When mock expectations fail, the error messages can be dense. Here's how to read them:
Error: Expected greet('[...]') once (never called)
greet(Bob) at MyTest.js:15
Reading the error:
Debugging steps:
verify() only once, in cleanup// BAD: Testing how the code works internally
mock.expects("_privateMethod").once();
mock.expects("helperFunction").twice();
// GOOD: Testing what the code does
const fake = sinon.fake.returns("result");
sinon.replace(obj, "publicMethod", fake);
// Assert on behavior, not internal calls
// BAD: Every interaction is an expectation
mock.expects("log").atLeast(1);
mock.expects("validateInput").once();
mock.expects("processData").once();
mock.expects("sendResponse").once();
// GOOD: Only mock what you need to control or verify
const fake = sinon.fake.resolves("result");
sinon.replace(service, "getData", fake);
// Explicit assertions on behavior
assert.ok(fake.called);
// BAD: Test breaks when call order changes
mock.expects("a").once();
mock.expects("b").once();
mock.expects("c").once();
// Now code must call in exact order: a, b, c
// GOOD: Test the outcome, not the path
const result = await service.process();
assert.equal(result, expectedValue);