Awesome
<p align="center"> <img alt="Mockt" src="docs/logo_readme.png" width="80%"> </p> <!-- A spacer --> <p> </p> <h2 align="center">Joyful mocking library for Typescript and Javascript</h2>Getting Started
Npm:
$ npm install --save-dev mockt
Yarn:
$ yarn add -D mockt
Now let's write a simple test:
const calculator = mockt(Calculator)
when(calculator).sum(2, any()).returns(3)
const actual = calculator.sum(2, 8)
expect(actual).toEqual(3)
verify(calculator).sum(any(), 8)
Features
- Typescript Support and IDE Autocompletion
- Class mocks
- Abstract class mocks
- Interface mocks
- Generics support
- Flexible stubbing with multiple returns, custom function call, errors, promises and stub overriding.
- Properties stubbing
- Spy on real objects
- Argument Capturing
- Verification atLeast, atMost, once, times(n)
- Verification called and never called
- Properties read and write verification
- Verification order
- Argument Matchers for stubs and verifications
- Reset mock stubs
- Reset mock recorded calls
Table of Contents
- Usage
- Class mocks
- Abstract Class mocks
- Interface mocks
- Generics
- Properties
- Stubbing
- Matchers
- Verification
- Verify method called
- Verify property read
- Verify property set
- Verify method called at least n times
- Verify method called at most n times
- Verify method called exactly n times
- Verify method never called
- Verify multiple method calls
- Verify multiple methods never called
- Verify multiple methods called in expected order
- Verify sequence of calls from different mocks or spies
- Capture arguments
- Spies
- Reset
Usage
Class mocks
class Calculator {
sum(a: number, b: number): number {
return a + b
}
}
const calculator = mockt(Calculator)
when(calculator).sum(2, 3).returns(5)
calculator.sum(2, 3) // returns 5
Arrow methods
class Calculator {
sum = (a: number, b: number) => a + b
}
const calculator = mockt(Calculator)
when(calculator).sum(2, 3).returns(5)
calculator.sum(2, 3) // returns 5
Note: arrow methods fails when called without stubbing them.
Abstract Class mocks
abstract class Calculator {
abstract sum(a: number, b: number): number
}
const calculator = mockt(Calculator)
when(calculator).sum(2, 3).returns(5)
calculator.sum(2, 3) // returns 5
Note: abstract methods fails when called without stubbing them.
Interface mocks
interface Calculator {
sum(a: number, b: number): number
}
const calculator = mockt<Calculator>()
when(calculator).sum(2, 3).returns(5)
calculator.sum(2, 3) // returns 5
Note: Interfaces are passed as Generic Parameter to mockt function.
Note: Interface methods fails when called without stubbing them.
Generics
interface List<T> {
add(item: T): void
get(index: number): T
}
const list = mockt(List<string>)
when(list).get(3).returns('Item 3')
list.get(3) // returns 'Item 3'
Properties
class User {
public name: string
}
const user = mockt(User)
when(user).name.returns('Alice')
user.name // returns 'Alice'
Getters
class User {
private _name: string
get name(): string {
return this._name
}
}
const user = mockt(User)
when(user).name.returns('Alice')
user.name // returns 'Alice'
Stubbing
Multiple returns
const calculator = mockt(Calculator)
when(calculator).sum(2, 3).returns(5, 6, 7)
calculator.sum(2, 3) // returns 5
calculator.sum(2, 3) // returns 6
calculator.sum(2, 3) // returns 7
calculator.sum(2, 3) // returns 7
Stub overriding
const calculator = mockt(Calculator)
when(calculator).sum(2, 3).returns(5)
when(calculator).sum(2, 3).returns(6)
calculator.sum(2, 3) // returns 6
calculator.sum(2, 3) // returns 6
const calculator = mockt(Calculator)
when(calculator).sum(any(), any()).returns(5, 6)
when(calculator).sum(2, 3).returns(7)
calculator.sum(2, 3) // returns 7
calculator.sum(2, 4) // returns 5
calculator.sum(2, 5) // returns 6
Function call
const calculator = mockt(Calculator)
when(calculator).sum(any(), any()).calls((a, b) => a * b)
calculator.sum(2, 3) // returns 6
calculator.sum(3, 4) // returns 12
Throw error
const calculator = mockt(Calculator)
when(calculator).sum(any(), any()).throws(new Error('Some error'))
calculator.sum(2, 3) // throws Error('Some error')
Promise resolve
const apiClient = mockt(ApiClient)
when(apiClient).getUser().resolves(user1, user2)
await apiClient.getUser() // returns user 1
await apiClient.getUser() // returns user 2
Promise reject
const apiClient = mockt(ApiClient)
when(apiClient).getUser().rejects(new Error('Some error'))
await apiClient.getUser() // throws Error('Some error')
Chained answers
const calculator = mockt(Calculator)
when(calculator).sum(2, 3)
.returns(5)
.returns(6)
.calls(() => 7)
.throws(new Error('Some error'))
calculator.sum(2, 3) // returns 5
calculator.sum(2, 3) // returns 6
calculator.sum(2, 3) // returns 7
calculator.sum(2, 3) // throws Error('Some error')
Matchers
Equals
const calculator = mockt(Calculator)
when(calculator).sum(2, eq(3)).returns(5)
calculator.sum(2, 3) // returns 5
calculator.sum(2, 4) // returns undefined
Note: When a value is given instead of a matcher, the equals matcher is used. Note: Performs deep equality
Deep equality and type conversion
const billingService = mockt(BillingService)
when(billingService).calculateFor(eq({ user: { age: '17' } })).returns(100)
billingService.calculateFor({ user: { age: 17 } }) // returns 100
Not Equals
const calculator = mockt(Calculator)
when(calculator).sum(2, neq(3)).returns(5)
calculator.sum(2, 6) // returns 5
calculator.sum(2, 3) // returns undefined
Identical
const billingService = mockt(BillingService)
const alice = { name: 'Alice' }
when(billingService).calculateFor(is(alice)).returns(100)
billingService.calculateFor(alice) // returns 100
billingService.calculateFor({ name: 'Alice' }) // returns undefined
Any
const calculator = mockt(Calculator)
when(calculator).sum(2, any()).returns(5)
calculator.sum(2, 5) // returns 5
calculator.sum(2, 6) // returns 5
calculator.sum(3, 5) // returns undefined
Any Number
const calculator = mockt(Calculator)
when(calculator).sum(anyNumber(), any()).returns(5)
calculator.sum(1, 2) // returns 10
calculator.sum('1', 2) // returns undefined
calculator.sum(1, '2') // returns 10
Any String
const splitter = mockt(StringSplitter)
when(splitter).split(anyString()).returns(['Hello', 'World'])
splitter.split('Hello World') // returns ['Hello', 'World']
splitter.split('Bye') // returns ['Hello', 'World']
splitter.split(3) // returns undefined
Any Array
const calculator = mockt(Calculator)
when(calculator).average(anyArray()).returns(10)
calculator.average([1, 2]) // returns 10
calculator.average([3, 4]) // returns 10
calculator.average([]) // returns 10
calculator.average(3) // returns undefined
Any Object
const billingService = mockt(BillingService)
when(billingService).calculateFor(anyObject()).returns(2000)
billingService.calculateFor({ name: 'Alice', lastname: 'Jones' }) // returns 2000
billingService.calculateFor({}) // returns 2000
billingService.calculateFor('alice') // returns undefined
Any Function
const caller = mockt(FunctionCalled)
when(caller).call(anyFunction()).returns(10)
caller.call(() => {}) // returns 10
caller.call(function () {}) // returns 10
caller.call('alice') // returns undefined
IsNull
const billingService = mockt(BillingService)
when(billingService).calculateFor(isNull()).throws(new Error('User cannot be null'))
billingService.calculateFor(null) // throws Error('User cannot be null')
billingService.calculateFor({ name: 'Alice' }) // Doesn't throw
billingService.calculateFor(undefined) // Doesn't throw
billingService.calculateFor('') // Doesn't throw
billingService.calculateFor([]) // Doesn't throw
IsNil
const billingService = mockt(BillingService)
when(billingService).calculateFor(isNil()).throws(new Error('User cannot be null'))
billingService.calculateFor(null) // throws Error('User cannot be null')
billingService.calculateFor(undefined) // throws Error('User cannot be null')
billingService.calculateFor({ name: 'Alice' }) // Doesn't throw
billingService.calculateFor('') // Doesn't throw
billingService.calculateFor([]) // Doesn't throw
NotNull
const billingService = mockt(BillingService)
when(billingService).calculateFor(notNull()).returns(5000)
billingService.calculateFor({ name: 'Alice' }) // returns 5000
billingService.calculateFor(undefined) // returns 5000
billingService.calculateFor(null) // returns undefined
NotNil
const billingService = mockt(BillingService)
when(billingService).calculateFor(notNil()).returns(5000)
billingService.calculateFor({ name: 'Alice' }) // returns 5000
billingService.calculateFor(undefined) // returns undefined
billingService.calculateFor(null) // returns undefined
Less
const calculator = mockt(Calculator)
when(calculator).sum(less(5), any()).returns(10)
calculator.sum(4, 2) // returns 10
calculator.sum(5, 2) // returns 10
calculator.sum(6, 2) // returns undefined
More
const calculator = mockt(Calculator)
when(calculator).sum(more(5), any()).returns(10)
calculator.sum(6, 2) // returns 10
calculator.sum(5, 2) // returns 10
calculator.sum(4, 2) // returns undefined
Range
const calculator = mockt(Calculator)
when(calculator).sum(range(5, 10), any()).returns(10)
calculator.sum(4, 2) // returns undefined
calculator.sum(5, 2) // returns 10
calculator.sum(6, 2) // returns 10
calculator.sum(9, 2) // returns 10
calculator.sum(10, 2) // returns 10
calculator.sum(11, 2) // returns undefined
OfClass
const billingService = mockt(BillingService)
when(billingService).calculateFor(ofClass(Employee)).returns(2000)
billingService.calculateFor(new Employee('Alice')) // returns 2000
billingService.calculateFor(new User('Alice')) // returns undefined
Nested matchers
const billingService = mockt(BillingService)
when(billingService).calculateFor(eq({ name: 'Alice', lastname: any() })).returns(2000)
billingService.calculateFor({ name: 'Alice', lastname: 'Jones' }) // returns 2000
const calculator = mockt(Calculator)
when(calculator).average(eq([1, any(), 3])).returns(1)
calculator.average([1, 2, 3]) // returns 1
Not
const calculator = mockt(Calculator)
when(calculator).sum(not(anyNumber()), any()).returns(10)
calculator.sum('5', 2) // returns 10
calculator.sum(5, 2) // returns undefined
And
const calculator = mockt(Calculator)
when(calculator).sum(and(neq(2), neq(3)), any()).returns(10)
calculator.sum(1, 2) // returns 10
calculator.sum(2, 2) // returns undefined
calculator.sum(3, 2) // returns undefined
Or
const calculator = mockt(Calculator)
when(calculator).sum(or(2, 3), any()).returns(10)
calculator.sum(2, 2) // returns 10
calculator.sum(3, 2) // returns 10
calculator.sum(1, 2) // returns undefined
Verification
Verify method called
const calculator = mockt(Calculator)
calculator.sum(1, 2)
verify(calculator).sum(1, 2) // passes
verify(calculator).sum(any(), 2) // passes
verify(calculator).sum(2, 2) // fails
Verify property read
const user = mockt(User)
const name = user.name
verify(user).getProperty('name') // passes
verify(user).getProperty('lastname') // fails
Verify property set
const user = mockt(User)
user.name = 'Alice'
verify(user).setProperty('name', 'Alice') // passes
verify(user).setProperty('name', any()) // passes
verify(user).setProperty('name', 'Bob') // fails
Verify method called at least n times
const calculator = mockt(Calculator)
calculator.sum(1, 2)
calculator.sum(3, 4)
calculator.sum(5, 6)
verifyAtLeast(2, calculator).sum(any(), any()) // passes
verifyAtLeast(3, calculator).sum(any(), any()) // passes
verifyAtLeast(4, calculator).sum(any(), any()) // fails
Verify method called at most n times
const calculator = mockt(Calculator)
calculator.sum(1, 2)
calculator.sum(1, 3)
calculator.sum(1, 4)
verifyAtMost(4, calculator).sum(1, any()) // passes
verifyAtMost(3, calculator).sum(1, any()) // passes
verifyAtMost(2, calculator).sum(1, any()) // fails
Verify method called exactly n times
const calculator = mockt(Calculator)
calculator.sum(1, 2)
calculator.sum(1, 3)
calculator.sum(1, 4)
verifyTimes(3, calculator).sum(1, any()) // passes
verifyTimes(2, calculator).sum(1, any()) // fails
Verify method never called
const calculator = mockt(Calculator)
calculator.sum(1, 2)
calculator.sum(1, 3)
calculator.sum(1, 4)
verifyNever(calculator).sum(1, 1) // passes
verifyNever(calculator).sum(1, 2) // fails
Verify multiple method calls
const calculator = mockt(Calculator)
calculator.sum(1, 2)
calculator.sum(1, 3)
calculator.sum(1, 4)
verifyMulti(calculator)
.sum(1, 2)
.sum(1, 4)
.called() // passes
Verify multiple methods never called
const calculator = mockt(Calculator)
calculator.sum(1, 2)
calculator.sum(1, 3)
calculator.sum(1, 4)
verifyMulti(calculator)
.sum(2, 2)
.sum(2, 4)
.never() // passes
Verify multiple methods called in expected order
const calculator = mockt(Calculator)
calculator.sum(1, 2)
calculator.sum(1, 3)
calculator.sum(1, 4)
verifyMulti(calculator)
.sum(1, 2)
.sum(1, 4)
.calledInOrder() // passes
const calculator = mockt(Calculator)
calculator.sum(1, 2)
calculator.sum(1, 3)
calculator.sum(1, 4)
verifyMulti(calculator)
.sum(1, 4)
.sum(1, 2)
.calledInOrder() // fails
Verify sequence of calls from different mocks or spies
import { verifySequence } from './verifySequence'
const billingService = mockt<BillingService>()
const deliveryService = mockt<DeliveryService>()
billingService.createFor(customer, products)
deliveryService.deliveryFor(customer.address, products)
verifySequence()
.call(billingService).createFor(any(), any())
.call(deliveryService).deliveryFor(any(), any()) // passes
Capture arguments
Last call
const calculator = mockt(Calculator)
calculator.sum(1, 2)
calculator.sum(1, 3)
const [first, second] = capture(calculator).sum // returns [1, 3]
const calculator = mockt(Calculator)
calculator.sum(1, 2)
calculator.sum(1, 3)
const [first, second] = captureLast(calculator).sum // returns [1, 3]
First call
const calculator = mockt(Calculator)
calculator.sum(1, 2)
calculator.sum(1, 3)
const [first, second] = captureFirst(calculator).sum // returns [1, 2]
All calls
const calculator = mockt(Calculator)
calculator.sum(1, 2)
calculator.sum(1, 3)
const args = captureAll(calculator).sum // returns [[1, 2], [1, 3]]
Setters
const user = mockt(User)
user.name = 'Alice'
const [name] = capture(user).setProperty('name') // returns 'Alice'
Spies
const calculator = new Calculator()
const calculatorSpy = spy(calculator)
calculator.sum(1, 2) // returns 3
verify(calculatorSpy).sum(1, 2) // passes
verify(calculatorSpy).sum(2, 2) // fails
Reset
Calls
const calculator = mockt(Calculator)
calculator.sum(1, 2) // returns undefined
verify(calculatorSpy).sum(1, 2) // passes
resetCalls(calculator)
verify(calculatorSpy).sum(1, 2) // fails
All
const calculator = mockt(Calculator)
calculator.sum(1, 2) // returns undefined
verify(calculatorSpy).sum(1, 2) // passes
reset(calculator)
verify(calculatorSpy).sum(1, 2) // fails
const calculator = mockt(Calculator)
when(calculator).sum(2, any()).returns(3)
reset(calculator)
calculator.sum(1, 2) // returns undefined
Credits
Thanks to all the contributors of ts-mockito and Mockk.