Before, we created a stub via Stub(someObject) and then we could call someObject.stub(...). This didn't make sense per definition. The definition is:
Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test.
We shouldn't have to call stub on an object to make it stub-able. Instead we should call Stub(theObjectReceivingTheStub, theDataMemberToStub, optionalValueTheStubShouldReturn). API looks like the below now:
class Server {
public greeting = "hello";
public methodThatLogs() {
console.log("Server running.");
}
}
const server = new Server();
Stub(server, "greeting", "you got changed");
assertEquals(server.greeting, "you got changed");
Stub(server, "greeting");
assertEquals(server.greeting, null);
// `is_stubbed` should be added when stubbing an object
assertEquals("is_stubbed" in server, true);
Use mixin for mock
Introduces using a mixin to create a mock so that calling private methods and returning this also works in a mock. For example, the old mock implementation wouldn't work when someComplexMethod() below would return this and call this.#setSomethingOne() and this.#setSomethingTwo() because the context of this was the mock object and not the original object. It needs to be an instance of the original object. That's where the mixin comes in. Basically, mocks are now extensions of the original so that mock instanceof original === true. This approach also fixes the getter/setter issue from https://github.com/drashland/rhum/pull/148/files.
class TestObjectThree {
public test(): string { return "World"; }
}
const mock = new MockBuilder(TestObjectThree).create();
assertEquals(mock.is_mock, true);
// Original returns "World"
assertEquals(mock.test(), "World");
// Don't fully pre-program the method. This should cause an error during
// assertions.
mock
.method("test")
.willReturn({
name: "something"
});
assertEquals(mock.test(), {name: "something"});
assertEquals(mock.calls.test, 2);
assertEquals(mock.calls.hello, 2);
Add mock.method(...).willThrow(...)
class TestObjectThree {
public test(): string { return "World"; }
}
const mock = new MockBuilder(TestObjectThree).create();
assertEquals(mock.is_mock, true);
// Original returns "World"
assertEquals(mock.test(), "World");
// Make the original method throw RandomError
mock
.method("test")
.willThrow(new RandomError("Random error message."))
assertThrows(
() => mock.test(),
RandomError,
"Random error message."
);
assertEquals(mock.calls.test, 2);
Add mock.expects(...).toBeCalled(...)
const mock = new MockBuilder(TestObjectThree).create();
assertEquals(mock.is_mock, true);
mock.expects("hello").toBeCalled(2);
mock.test(); // Calls .hello() under the hood twice
mock.verifyExpectations(); // Must be called to verify the .expects() calls
Add Fake()
Fakes kind of act like mocks, but they do not have calls and expectations like mocks.
// Assert that a fake can make a class take a shortcut
const fakeServiceDoingShortcut = Fake(Repository).create();
fakeServiceDoingShortcut.method("findAllUsers").willReturn("shortcut");
const resourceWithShortcut = new Resource(
fakeServiceDoingShortcut,
);
resourceWithShortcut.getUsers();
assertEquals(fakeServiceDoingShortcut.anotha_one_called, false);
assertEquals(fakeServiceDoingShortcut.do_something_called, false);
assertEquals(fakeServiceDoingShortcut.do_something_else_called, false);
// Assert that the fake service is not yet doing a shortcut
const fakeServiceNotDoingShortcut = Fake(Repository).create();
const resourceWithoutShortcut = new Resource(
fakeServiceNotDoingShortcut,
);
resourceWithoutShortcut.getUsers();
assertEquals(fakeServiceNotDoingShortcut.anotha_one_called, true);
assertEquals(fakeServiceNotDoingShortcut.do_something_called, true);
assertEquals(fakeServiceNotDoingShortcut.do_something_else_called, true);
Add Dummy()
Dummies are empty objects of an instance of something. For example, Dummy(Hello) instanceof Hello === true. This makes it a little bit easier to fill in parameter lists where parameters must be instances of something.
Overview of commits
Updated README
See the new README here.
Refactor and add features to be closer to test double definitions
Test double definitions can be found here: https://martinfowler.com/bliki/TestDouble.html
Currently, we only have:
Still need to add in spies.
Stub()
Before, we created a stub via
Stub(someObject)
and then we could callsomeObject.stub(...)
. This didn't make sense per definition. The definition is:We shouldn't have to call stub on an object to make it stub-able. Instead we should call
Stub(theObjectReceivingTheStub, theDataMemberToStub, optionalValueTheStubShouldReturn)
. API looks like the below now:Use mixin for mock
Introduces using a mixin to create a mock so that calling private methods and returning
this
also works in a mock. For example, the old mock implementation wouldn't work whensomeComplexMethod()
below would returnthis
and callthis.#setSomethingOne()
andthis.#setSomethingTwo()
because the context ofthis
was the mock object and not the original object. It needs to be an instance of the original object. That's where the mixin comes in. Basically, mocks are now extensions of the original so thatmock instanceof original === true
. This approach also fixes the getter/setter issue from https://github.com/drashland/rhum/pull/148/files.Add mock.method(...).willReturn(...)
Add mock.method(...).willThrow(...)
Add mock.expects(...).toBeCalled(...)
Add Fake()
Fakes kind of act like mocks, but they do not have calls and expectations like mocks.
Add Dummy()
Dummies are empty objects of an instance of something. For example,
Dummy(Hello) instanceof Hello === true
. This makes it a little bit easier to fill in parameter lists where parameters must be instances of something.Dummies can also be created without having to specify constructor arguments.