With TypeScript 3, you are already missing out when mocking or faking

Written by mathiaslykkegaardlorenzen | Published 2018/07/21
Tech Story Tags: typescript | javascript | mocking | software-testing | typescript-3

TLDRvia the TL;DR App

Before we start, I am aware of the following:

  • I used a click semi click-baity headline to get you here.
  • I am shamelessly sharing yet another mocking/faking framework (substitute.js) among many existing ones.
  • I am the author of that said framework.
  • I am not Facebook or Google, just a developer like you.
  • I’m not even a writer — you must have already guessed that part.

So why pay attention and even give me one more minute of your time?

Simple. I’m almost certain I can give that minute back to you, and plenty more in the future.

I won’t be going into details about what faking or mocking is, or why it’s a good idea. This post assumes you already know that.

Table of contents

Comparing frameworks with code samples

All of the examples below assume that there is a class called RealCalculator and an interface backing it up called CalculatorInterface. These are defined below respectively.

With these types defined, let’s get on with it. I hope the examples speak for themselves, and that you’ll agree with the following:

  • That substitute.js syntax is easier to remember, easier to learn and faster to write.
  • That the ability to fake/mock interfaces is a huge advantage over existing frameworks, due to its effect on TDD.

Creating a mock of a class

ts-mockito

With ts-mockito you first create a mock “class” or “constructor”, and you can then create a real non-fake instance from this mock.

typemoq

The same goes for typemoq.

substitute.js

For substitute.js, the instance and the fake is one and the same.

Creating a mock of an interface

Creating a mock from an interface makes TDD a lot easier with a lot less code, all while being able to compile along the way. With interface mocking, you can:

  1. Start by defining your interfaces (no implementation).
  2. Depend on those interfaces in your code.
  3. Write the tests for the interfaces using fakes (you can fake the interfaces without writing the classes first!).
  4. Implement the code with real classes.
  5. Smile. You were able to have full strong-typing and something that actually compiles all the way through the process. No squiggly lines, just pure pleasant TDD.

ts-mockito

This is unfortunately not supported.

typemoq

The same goes for typemoq — not supported.

substitute.js

In substitute.js, you can fake from an interface just fine. The magic behind this is possible due to ES6 proxies.

Stubbing methods

In this example we’ll compare how the three frameworks handle stubbing of methods.

ts-mockito

With ts-mockito you specify the arguments and return values on the mock object within wrapper functions.

typemoq

The same goes for typemoq.

substitute.js

For substitute.js, you can define your return values on the mock itself and yet use it in a fluent and strong typed manner.

Stubbing properties

In this example we’ll compare how the three frameworks handle stubbing of properties.

ts-mockito

With ts-mockito you specify the return values on the mock object within wrapper functions.

typemoq

The same goes for typemoq.

substitute.js

For substitute.js, you can define your return values on the mock itself and yet use it in a fluent and strong typed manner.

Throwing errors

In this example we’ll compare how the three frameworks handle throwing errors. The same syntax across all frameworks also apply for methods in a similar manner described above, so we won’t go through that.

ts-mockito

With ts-mockito you specify the exception to throw on the mock object within wrapper functions.

typemoq

The same goes for typemoq.

substitute.js

For substitute.js, you use the returns function and wrap it in a lambda to throw an error.

Call count verification

In this example we’ll compare how the three frameworks handle call count verification. The same syntax across all frameworks also apply for properties, so we won’t go through that.

Note that _substitute.js_ does not support the “at least _n_ times” verification nor the “fewer than _n_ times” verification because I personally believe it is bad practise — your tests should always be deterministic. You should always know the amount of calls to expect in a test.

ts-mockito

With ts-mockito you specify the operation to match on the mock object within wrapper functions.

typemoq

The same goes for typemoq.

substitute.js

For substitute.js, you use the receivedfunction and call the operation on its return value.

Alternatively, you can also use didNotReceive which does exactly what you would expect.

Proxying calls

Proxying calls can be useful if you want your fake method to do something else.

ts-mockito

With ts-mockito you specify the operation to match on the mock object within wrapper functions, and then use another function to specify which method to proxy calls to.

typemoq

The same goes for typemoq.

substitute.js

For substitute.js, you use the mimicksfunction and specify which function to proxy calls to.

Matching arguments

Argument matchers allow you to be more explicit about when you want to fake something, or what calls you expect to receive.

ts-mockito

With ts-mockito you use functions like anything and between to match arguments.

I have not found a way to specify a custom lambda to express a custom matcher yet in _ts-mockito_— let me know if you know of any in the comments section.

typemoq

Almost the same as ts-mockito, but supports lambdas.

substitute.js

Here, the syntax is nice and clear. A nice little detail is that there is strong-typing all the way even as you type, with full type inference (it even knows what type x is in the Arg.is function.

Errors produced by call verification

Proxying calls can be useful if you want your fake method to do something else.

ts-mockito

ts-mockito displays the method name and parameters as well as how many times a call was expected, but does not show the total calls that were recorded.

The above code throws:

Expected “add(strictEqual(1), strictEqual(2))” to be called at least 1 time(s). But has been called 0 time(s).

typemoq

The same goes for typemoq, except it also displays a list of calls recorded

The above code throws:

Expected invocation of RealCalculator.add(It.isValue(1),It.isValue(2)) at least 1 times, invoked 0 times

Configured setups:RealCalculator.add(It.isValue(1),It.isValue(2))

Performed invocations:RealCalculator.add(3,4)

substitute.js

For substitute.js, you use the mimicksfunction and specify which function to proxy calls to.

The above code will throw:

Expected one or more calls to the method c with arguments [1, 2], but received none of such calls. All calls received to method c: -> 1 call with arguments [3, 4]

Limitations

The above all looks quite good, right? But there are limitations in substitute.js like any other framework.

Below is a list all of the things the other frameworks can do that substitute.js can’t, and a reason for not supporting it.

Partial mocks

Partial mocks are typically bad if you rely on only a few faked methods. Typically when testing, you want to fake either everything or almost everything.

While substitute.js does not support partial mocks, you can explicitly proxy each method you want to “mimick” from the real instance.

Call order verification

The outside world (using the public interface of your unit) should not care about which order your unit calls its dependencies’ methods. I see this as an anti-pattern, which is why it is not supported.

More?

Are there more examples that substitute.js can’t do that the others can? Let me know in the comments, and I’ll add it or justify why it hasn’t been added to the framework.

substitute.js tries to make the types of faking operations you use 99% of the times simple, but it should still be flexible enough to cover complex cases, if one is willing to sacrifice the good-looking syntax.

Getting substitute.js

Here’s the command you’ll need to install substitute.js . Note that it requires TypeScript 3 or later.

npm install @fluffy-spoon/substitute --save-dev

GitHub: https://github.com/ffMathy/FluffySpoon.JavaScript.Testing.FakingNPM: https://www.npmjs.com/package/@fluffy-spoon/substitute


Published by HackerNoon on 2018/07/21