@johanblumenberg/ts-mockito
Fork of ts-mockito, which will be kept until the following PRs are accepted, or similar functionality is added to ts-mockito:
Installation
npm install @johanblumenberg/ts-mockito --save-dev
Added functionality in this fork
Verify with timeout
This feature is useful when testing asynchronous functionality.
You do some action and expect the result to arrive as an asynchronous
function call to one of your mocks.
let mockedFoo:Foo = mock(Foo);
await verify(mockedFoo.getBar(3)).timeout(1000);
Mocking interfaces
Mocking interfaxces works just the same as mocking classes, except you
must use the imock()
function to create the mock.
let mockedFoo:Foo = imock();
when(mockedFoo.getBar(5)).thenReturn('five');
It also works for properties.
let mockedFoo:Foo = imock(MockPropertyPolicy.StubAsProperty);
when(mockedFoo.bar).thenReturn('five');
For interface mocks, you can set the defauklt behviour for mocked properties that
have no expectations set. They can behave eiter as a property, returning null, or
as a function, returning a function that returns null, or throw an exception.
let mockedFoo1:Foo = imock(MockPropertyPolicy.Throw);
instance(mockedFoo1).bar;
let mockedFoo2:Foo = imock(MockPropertyPolicy.Throw);
when(mockedFoo2.bar).thenReturn('five');
instance(mockedFoo2).bar;
let mockedFoo3:Foo = imock(MockPropertyPolicy.StubAsProperty);
instance(mockedFoo3).bar;
let mockedFoo4:Foo = imock(MockPropertyPolicy.StubAsMethod);
instance(mockedFoo4).getBar(5);
Mocking free functions
Sometimes you need to mock a function, not an object, for example to pass as a callback somewhere. This can be done using fnmock()
. It works just like any other mock, except it's a function, not an object.
let fn: (a: number, b: string) => number = fnmock();
when(fn(10, 'hello')).thenReturn(5);
instance(fn)(10, 'hello');
verify(fn(10, 'hello')).called();
Mocking constructors
Sometimes you need to mock a constructor, and control creation of new objects.
let mockedFooCtor: new () => Foo = cmock();
let mockedFoo:Foo = imock();
when(new mockedFooCtor()).thenReturn(instance(mockedFoo));
const result = new (instance(mockedFooCtor))();
verify(new mockedFooCtor()).called();
expect(result).toBe(instance(mockedFoo));
Defer resolving promises
The actions .thenResolve()
and .thenReject()
are returning promises that are already resolved or rejected. Sometimes you want to control the order or timing of when promises are resolved. In that case it is useful to return a deferred promise, and resolve it from the test code, when appropriate.
let d = defer<number>();
when(obj.method()).thenReturn(d);
d.resolve(1);
Mock React
props with enzyme
It's possible to mock props for react components when testing using enzyme.
let props: Props = imock(MockPropertyPolicy.StubAsProperty);
when(props.text).thenReturn('OK');
when(props.onClick()).thenReturn();
let c = mount(<MyButton {...instance(props)}>);
Usage
Basics
let mockedFoo:Foo = mock(Foo);
let foo:Foo = instance(mockedFoo);
foo.getBar(3);
foo.getBar(5);
verify(mockedFoo.getBar(3)).called();
verify(mockedFoo.getBar(5)).called();
Stubbing method calls
let mockedFoo:Foo = mock(Foo);
when(mockedFoo.getBar(3)).thenReturn('three');
let foo:Foo = instance(mockedFoo);
console.log(foo.getBar(3));
console.log(foo.getBar(999));
Stubbing getter value
let mockedFoo:Foo = mock(Foo);
when(mockedFoo.sampleGetter).thenReturn('three');
let foo:Foo = instance(mockedFoo);
console.log(foo.sampleGetter);
Stubbing property values that have no getters
Syntax is the same as with getter values.
Please note, that stubbing properties that don't have getters only works if Proxy object is available (ES6).
Call count verification
let mockedFoo:Foo = mock(Foo);
let foo:Foo = instance(mockedFoo);
foo.getBar(1);
foo.getBar(2);
foo.getBar(2);
foo.getBar(3);
verify(mockedFoo.getBar(1)).once();
verify(mockedFoo.getBar(2)).twice();
verify(mockedFoo.getBar(between(2, 3))).thrice();
verify(mockedFoo.getBar(anyNumber()).times(4);
verify(mockedFoo.getBar(2)).atLeast(2);
verify(mockedFoo.getBar(1)).atMost(1);
verify(mockedFoo.getBar(4)).never();
Call order verification
let mockedFoo:Foo = mock(Foo);
let mockedBar:Bar = mock(Bar);
let foo:Foo = instance(mockedFoo);
let bar:Bar = instance(mockedBar);
foo.getBar(1);
bar.getFoo(2);
verify(mockedFoo.getBar(1)).calledBefore(mockedBar.getFoo(2));
verify(mockedBar.getFoo(2)).calledAfter(mockedFoo.getBar(1));
verify(mockedFoo.getBar(1)).calledBefore(mockedBar.getFoo(999999));
Throwing errors
let mockedFoo:Foo = mock(Foo);
when(mockedFoo.getBar(10)).thenThrow(new Error('fatal error'));
let foo:Foo = instance(mockedFoo);
try {
foo.getBar(10);
} catch (error:Error) {
console.log(error.message);
}
Custom function
You can also stub method with your own implementation
let mockedFoo:Foo = mock(Foo);
let foo:Foo = instance(mockedFoo);
when(mockedFoo.sumTwoNumbers(anyNumber(), anyNumber())).thenCall((arg1:number, arg2:number) => {
return arg1 * arg2;
});
console.log(foo.sumTwoNumbers(5, 10));
Resolving / rejecting promises
You can also stub method to resolve / reject promise
let mockedFoo:Foo = mock(Foo);
when(mockedFoo.fetchData("a")).thenResolve({id: "a", value: "Hello world"});
when(mockedFoo.fetchData("b")).thenReject(new Error("b does not exist"));
Resetting mock calls
You can reset just mock call counter
let mockedFoo:Foo = mock(Foo);
let foo:Foo = instance(mockedFoo);
foo.getBar(1);
foo.getBar(1);
verify(mockedFoo.getBar(1)).twice();
resetCalls(mockedFoo);
verify(mockedFoo.getBar(1)).never();
Resetting mock
Or reset mock call counter with all stubs
let mockedFoo:Foo = mock(Foo);
when(mockedFoo.getBar(1)).thenReturn("one").
let foo:Foo = instance(mockedFoo);
console.log(foo.getBar(1));
console.log(foo.getBar(1));
verify(mockedFoo.getBar(1)).twice();
reset(mockedFoo);
verify(mockedFoo.getBar(1)).never();
console.log(foo.getBar(1));
Capturing method arguments
let mockedFoo:Foo = mock(Foo);
let foo:Foo = instance(mockedFoo);
foo.sumTwoNumbers(1, 2);
const [firstArg, secondArg] = capture(mockedFoo.sumTwoNumbers).last();
console.log(firstArg);
console.log(secondArg);
You can also get other calls using first()
, second()
, byCallIndex(3)
and more...
Recording multiple behaviors
You can set multiple returning values for same matching values
const mockedFoo:Foo = mock(Foo);
when(mockedFoo.getBar(anyNumber())).thenReturn('one').thenReturn('two').thenReturn('three');
const foo:Foo = instance(mockedFoo);
console.log(foo.getBar(1));
console.log(foo.getBar(1));
console.log(foo.getBar(1));
console.log(foo.getBar(1));
Another example with specific values
let mockedFoo:Foo = mock(Foo);
when(mockedFoo.getBar(1)).thenReturn('one').thenReturn('another one');
when(mockedFoo.getBar(2)).thenReturn('two');
let foo:Foo = instance(mockedFoo);
console.log(foo.getBar(1));
console.log(foo.getBar(2));
console.log(foo.getBar(1));
console.log(foo.getBar(1));
console.log(foo.getBar(2));
console.log(foo.getBar(2));
Short notation:
const mockedFoo:Foo = mock(Foo);
when(mockedFoo.getBar(anyNumber())).thenReturn('one', 'two', 'three');
const foo:Foo = instance(mockedFoo);
console.log(foo.getBar(1));
console.log(foo.getBar(1));
console.log(foo.getBar(1));
console.log(foo.getBar(1));
Possible errors:
const mockedFoo:Foo = mock(Foo);
when(mockedFoo.getBar(anyNumber())).thenReturn('one');
when(mockedFoo.getBar(3)).thenReturn('one');
const foo:Foo = instance(mockedFoo);
foo.getBar(3);
Mocking types
You can mock abstract classes
const mockedFoo: SampleAbstractClass = mock(SampleAbstractClass);
const foo: SampleAbstractClass = instance(mockedFoo);
You can also mock generic classes, but note that generic type is just needed by mock type definition
const mockedFoo: SampleGeneric<SampleInterface> = mock(SampleGeneric);
const foo: SampleGeneric<SampleInterface> = instance(mockedFoo);
Spying on real objects
You can partially mock an existing instance:
const foo: Foo = new Foo();
const spiedFoo = spy(foo);
when(spiedFoo.getBar(3)).thenReturn('one');
console.log(foo.getBar(3));
console.log(foo.getBaz());
You can spy on plain objects too:
const foo = { bar: () => 42 };
const spiedFoo = spy(foo);
foo.bar();
console.log(capture(spiedFoo.bar).last());
Thanks