Testing your TypeScript code with ts-mockito

Harijs Deksnis
Passionate People
Published in
6 min readMar 15, 2019

--

Hi, iam@harijs.dev and I work for PassionatePeople. You can follow me on Twitter as @FrontendNL

This post is based on my talk at AngularNL conference in Amsterdam on 8th of March, 2019. The first part of the series can be found here.
The talk was about practical lessons from building Angular apps, but I’d like to keep this particular article framework-agnostic as much as possible, since nowadays TypeScript is used for a very wide variety of applications.

Some of the code examples in this article will be based on Angular specific cases, but there’s nothing that prevents you to apply the same concepts to other TypeScript projects.

Photo by Shane Aldendorff on Unsplash

Why (unit) test at all?

This question is essentially out of the scope of this article and is discussed in countless other articles. However, the main reasons why I insist on strong unit test coverage and where I see most benefits are:

Helps with refactoring

You are more at ease to move various codebase fragments around as the requirements change when there’s a good test coverage. This way you can be more safe to not break parts that should operate the same after you are done with refactoring. Also seeing tests fail in the process should give you a better understanding what are the ramifications of your changes and if something unexpected is happening.

Helps to find bugs earlier

By specifying various test cases how your component should behave in multiple scenarios, you stand more chances of discovering any present bugs earlier.

Helps with debugging

If your codebase breaks and tests suddenly start to fail and it’s hard to tell why, you can go back in history to earlier commits to see when tests started to fail exactly and which change-set is responsible for that.

There are more reasons why we should do a good job with unit testing obviously, but these are the 3 main ones I appreciate the most from my own developer’s perspective.

Where does ts-mockito come in?

The core premise of unit testing is that you should test your components in isolation. That means that any dependencies you might have, anything outside of the testable component should be mocked. That presents a certain overhead. Not only we are supposed to write explicit test cases, but also to write mock classes and mock dependencies that our component will consume during tests. That can cost a lot of time and effort to do well.

Angular guide on testing has plenty of examples and recipes how to do it with Jasmine and spies, which is a completely valid way to do it, but I have found ts-mockito to be a more convenient alternative. It helps us to make mocks seamlessly and declaratively. And what is more, it’s useful for any TypeScript code in any testing setup. So it’s applicable to any TypeScript project out there.

Since it’s a generic mocking library for any TypeScript code, you can maintain and export your mocks from a single file and use throughout the codebase without the need to write any framework or testing-framework specific code for your mocks.

How to use it?

Creating a mock is very straightforward and seamless. You just wrap it in a mock function:

That will create a mock that is strongly typed and matches the signature of the entity you are trying to mock. In vast majority of the cases that’s all you will need to do and won’t have to spend more time to make TypeScript compiler happy.

In Angular context, passing the mocked entity to the test suite can be done as follows:

You need to wrap it in an instance function and pass it as a value in providers array. And it is very important to reset your mock after each test case. Failing to do so might create side effects from one test case to spill over to another. Therefore, always reset your mocks after each test case is run.

For testing modules, directives and other entities, as well as with any other non-Angular Typescript code where you must instantiate a new instance of whatever you are testing, you will find yourself most of the times doing something like this:

Features

There is a wide range of useful features that come in handy in various testing scenarios. Here I will list the primary ones that will help you write solid test cases.

Mock behaviour

Mocking certain behaviour is very straightforward and is declared in a “when x — then y” type of way. By using when function you can specify a behaviour what should happen when a specific method gets called on the mockable entity. For example: when(MockedArticleService.getArticles()).thenReturn([]);

This lets you specify what the mock should return when a particular method gets called. Besides thenReturn you can also specify thenThrow, thenCall, thenResolve and thenReject. More elaborate examples for each can be found on GitHub.

What is more, the when part can be defined more specifically by providing arguments, or argument types to tailor responses to specific call signatures. For example: when(MockedArticleService.getInCategory('travel').thenReturn([..])
or more generally:
when(MockedArticleService.getInCategory(anyString()).thenReturn([..])

Besides anyString call signature matcher you can use anyFunction, anyNumber, anything, anyOfClass, notNull and few others. Their names should be self explanatory:

ts-mockito type declarations for various call signature matchers

Here are examples how to specify different mocked behaviour:

Record multiple behaviours

The return signature doesn’t need to be static. ts-mockito allows you to define a series of behaviours for when the same method gets called multiple times. You can specify what it returns after first, second, third etc.. invocations with the last one being returned from thereon.

Verify how methods are being called

verify function allows to assert that certain methods are being called with specific arguments, certain types, total number of times and in a specific order.

You can use it with already mentioned matchers like anyString, anyNumber and others. With verify you can add such chained assertions as once, twice, times, atLeast and others to assert number of times a method is expected to be called. If you want to verify the order in which methods should be called, use calledBefore and calledAfter to do exactly that.

Ways to verify method calls

Here are some code examples how you would go about using verify:

If any of the verify specified clauses is not fulfilled, it will throw an error. And any unhandled error will fail your test case. So using ts-mockito and verify will work with any testing framework.

Capturing arguments

Lastly you might want to capture arguments and make assertions that mocked methods are being called with the right ones. The basic way how it is done is:
const args = capture(MockedEntity.mockedMethod).last();

Besides capturing last method call, you can capture others with first, second, third, beforeLast and byCallIndex functions. Here are some examples:

Conclusion

ts-mockito is a very useful library with a witty name that allows you to write mocks for your test cases with ease. By simplifying mocking aspect it allows you to spend more time and effort on writing solid test cases and bug-proofing your codebase. It provides a wide variety of approaches to thoroughly test your code and it can be used for any TypeScript project.

I personally have found it to be very useful in my development practice. If you haven’t yet tried it and are open to exploration, I highly suggest experimenting with it for your next TypeScript project.

I hope you enjoyed this article. Shall you have any feedback, please don’t hesitate to leave a comment.

Quick links:

This is second article in the series based on my conference talk. First one can be found here.

I am a developer working for PassionatePeople agency in Amsterdam and we build awesome apps with all major frameworks for a variety of clients. You can drop me an email to iam@harijs.dev, follow me @FrontendNL or check out my LinkedIn and Github profile page.

📝 Read this story later in Journal.

🗞 Wake up every Sunday morning to the week’s most noteworthy Tech stories, opinions, and news waiting in your inbox: Get the noteworthy newsletter >

--

--