Typescript Types Mock… for real

Vittorio Guerriero
5 min readApr 14, 2019

--

Photo by Kevin Ku from Pexels

Mocking interfaces/classes in your unit test could be annoying.

You often need to create your mock version and end up with a lot of mock object data in your test.

😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭😭

Problem

We have a simple person interface that is being used by a function that returns some of the information…

interface Person {
id: string;
getName(): string;
getAge(): number;
getChildren(): Person[];
}
function PersonInformation(person: Person): string {
return `id=${person.id} - name=${ person.getName()}';
}

…in your test you need to provide a person object with methods that satisfy the interface completely unless you use casting (you are evil 😈 ).

Even with casting you need to provide the implementation of methods and properties that you need for the test.

const mockPerson: Person = {
id: '',
getName: () => '',
getAge: () => 0,
getChildren: () => [],
};
const personInformation: string = PersonInformation(mockPerson);personInformation// 'id="" - name=""'

Typescript doesn’t keep interfaces after compilation so it’s not possible to create basic mocks from interfaces without giving them an implementation

What if there was a way to generate mocks from interface to make test development faster and nicer?

If I was creating a mock of Person from my imaginary mock creator I would like to automatically have the minimum default implementation like this example

const mock: Person = myImaginaryMockCreator<Person>();
mock.id // ""
mock.getName() // ""
mock.getAge // 0
mock.getChildren() // []

🤑

If I had this functionality at least my test would not fail for missing implementation

Is this a dream?? No

Solution

How do we improve our test? How do we automate our mocks so we can concentrate on the test instead of the mock objects?

There is a solution, and its called ts-auto-mock 💪

Ts-auto-mock

The intention of Ts-auto-mock is to create mocks from types. It doesn’t want to replace your test framework.

It’s a library that takes advantages of typescript transformer and it creates mocks for you. Let’s have a look of a basic usage and then we will talk about installation

import { createMock } from 'ts-auto-mock';const mock: Person = createMock<Person>();

Done. You call createMock with any type and it will create a basic mock with default values.

Installation

  1. install ts-auto-mock

Simple as npm install ts-auto-mock -D -E

2. Add the transformer ts-auto-mock/transformer to your compilation

This part is a bit tricky but there are different solutions based on what you are using. link

For the purpose of this article I’m not going to explain how to configure it in depth, hence I will assume that the transformer is already part of the compilation

Usage

import { createMock } from 'ts-auto-mock';const mock: Person = createMock<Person>();

createMock will create automatically:

  1. Object properties
  2. Methods properties
  3. Nested objects

If you have a recursive interface ts-auto-mock will make sure that the nested object is always present

interface Employee {
position: string;
boss: Employee
};
const mock: Employee = createMock<Employee>();mock.boss.boss.boss.boss........... // It will not fail. It exists

This is the basic feature of ts-auto-mock. The library goes a bit further.

Extensions

Mostly of the time you will have your framework (jasmine, jest, aNewBeautifulTestFramework, etc ).

There are already two libraries that extends ts-auto-mock:

You can use them and follow their documentation.

If you want to understand how the extension works keep reading…

ts-auto-mock expose a factory where we can decide what it will happened every time it finds a method

Configure your mock

If we want to have a jasmine.Spy instead of the default method, we can configure ts-auto-mock to do it (this is what jasmine-ts-auto-mock and jest-ts-auto-mock do)

Note: This file needs to run before your tests.

import { MockFactory } from 'ts-auto-mock';MockFactory.instance.registerFactory((name: string, value: any) => {  return jasmine.createSpy(name).and.returnValue(value);});

Provide your return type

declare module 'ts-auto-mock' {
interface MockMethod<TR> extends jasmine.Spy
}

Get the mock safely

The spy method exists inside the mock object but ts-auto-mock wants to be type safe and makes sure that you have a clear division between the instance of the mock (Person) and the customised mocked version.

Ts-auto-mock provide a small utility to get your functionality.

— — — — — — — — With a string- — — — — — — —

import { On, method } from 'ts-auto-mock';let nameSpy = On(mockPerson).get(method('getName'));

— — — — — — — -With method selection — — — — — — —

import { On, method } from 'ts-auto-mock';nameSpy = On(mockPerson).get(method(mock => mock.getName));

Done.

Now if we test again our methods we can get the spies

import { createMock, On, method } from 'ts-auto-mock';interface Person {
getName(): string;
getSurname(): string;
}

function personFullName(person: Person): string {
return person.getName() + person.getSurname();
}
const mock: Person = createMock<Person>();
const nameSpy: jasmine.Spy = On(mock).get(method('getName'));
const surnameSpy: jasmine.Spy = On(mock).get(method('getSurname'));
personFullName(person);// ""
expect(nameSpy).toHaveBeenCalled();
expect(surnameSpy).toHaveBeenCalled();

Conclusion

Ts-auto-mock has few benefits:

  • Real mocks types
  • Less code in your unit test
  • Recursive mocks
  • Can be extended. Is not coupled to a specific testing framework

The main disadvantages is the configuration that hopefully will be solved once Typescript will add support for custom plugin directly in the tsconfig.

About the library

These libraries have been created by me and Giulio Caprino. (It wouldn’t be possible without him).

We are planning to add new feature to this library to improve performance and more automatic custom mocks

Also I would like to thanks the creator of ts-transformers-keys. I’ve used his library for the first couple of days to understand a bit more on how to create a typescript transformer

Another special thanks to the creator of ttypescript. Thanks to his library jest-ts-auto-mock can be used with a rather easy configuration along jest

Links

--

--