Unit tests for SkyNet (written in JS)

Anton Korzunov
ITNEXT
Published in
6 min readMar 29, 2018

--

I am going to reveal a truth about the rise and fall of SkyNet, and why Terminator 1 was followed by Terminator 2, 3, Genesis and so on. The secret is….

SkyNet was not a "subject under tests".

And, like anything that is not properly tested, it got broken shortly after being released to production. That’s common. The point here — how to test something before going live.

You shall be prepared well to nuke all humans, cos there will be no second chance. (however, if only you don’t have a time machine)

TDD — Test Driven Development

First, let's clarify what SkyNet shall do:

  1. When the doom day comes — launch the rockets. Nuke humans.
  2. When John Connor is born — kill him. And film a movie.

But there is a problem — all these actions could be executed once. And, if something goes wrong, SkyNet will not be able to conqueror the world.

How to test SkyNet then?

So — SkyNet has only one chance to launch the rockets, and only one chance to kill John Connor. Just because we (humankind) don’t have more rockets, and John is also the single one.

As a result — the only way to test SkyNet before the doomsday — is to mock all used equipment. To make tests repeatable.

The SkyNet Code

Here they are — the rocket launcher, and rocket-launcher launcher.

When TheDay comes, it will rocket rockets. And the only way to test this scenario is to mock ./core and ./rocket-silo. Convert them to fakes. Test doubles.

How to mock?

I'm glad you asked. There are a few different ways to mock, way more libraries to do it, and a dozen of patterns and anti-patterns.

Variant 1 – use mocked realization, instead of a real endpoint.

Just create a __mocks__/rocket, and it will replace the real file.

This is a super handy solution to stub the code, or mock actions. But not super handy to mock selectors, as long as you cannot modify this code from a test.

Easy. And Powerful.

But, just Jest could “manual mock” in such way, and if you don’t have Jest – you don’t have these mocks.

No Jest — no Fun.

Variant 2. Just mock?

Easy? Simple? Again Jest only!

The main drawback here – you cannot change your mind. Dependency will be mocked for the entire test, and you cannot unmock them.

Yet again – just jest could do it. The problem is with nodejs itself – mocks has to be defined before file import, but nothing could be higher than imports.

Jest solved the problem using Babel plugin.

Version 3 – just mock it somehow!

This time let’s use mockery.

The main drawback here is API – you have to first require define mocks, next require file, and only then – use it.

Version 4 – get the things mocked

What about proxyquire? It has a super handy syntax.

You can just get mocked file with mocked dependencies. And much more.

The main drawback for both mockery or proxyquire – you cannot be 100 percent sure that (and what) you are mocking. Mockery isolation mode could help with it, but it is not a silver bullet — it is a hammer.

Version 5 – if you don’t need that more.

TestDouble.js – testing library with a strong theory behind. It actually has a single one command to mock.

Td.replace is similar to Jest.mock – it just automocks everything.

Awwseeee

But how to mock out SkyNet tests?

If SkyNet uses Jest – it has to mock everything using Jest API. And if you are doing the same – you could close this article.

If SkyNet uses AVA, Mocha or Karma as a test runner – well, it still might wish to use Jest.mock, but cannot. That's unfair — you can’t use Jest tools without Jest, and you can’t use “other” tools inside Jest.

Behavior, closest to Jest, belongs to TD, but you might not like TD, and prefer Sinon.

Then, mockery or proxyquire? First get isolation mode (confidence and smells doubled), second mocks out only first nesting level (which is awesome, but not always).

It is very hard to choose one API against another. For many of you Jest or TD “auto”-”all”-”mocks” shall be preferred. Meanwhile, only Proxyquire allows “callThough” to the real file (this might be an anti-pattern). But I could propose another way.

Choose rewiremock instead.

Rewiremock is not a new mocking library, but it is far more younger than others. Thus it was able to learn from the predecessors and make things better.

It could replace Jest, even Jest manual mocks.

It could replace mockery

It could replace proxyquire

It could replace TD.

And it could do more

Top level API gives several commands to actually require file with mocked dependencies — .proxy, .around, .module to handle ANY case.

Isolation API to prevent any unexpected changes.

Type safety(!) for mocks. You are lucky to use typed language.

But the main treasure is not mocking API, but mock API. Let me explain this by example.

You can specify that mock have to be used. You can also specify where it shall be used — only for direct nested dependencies (like proxyquire), or only from mocked code(ie parent should be mocked), or elsewhere.

You can use “advanced” mock API everywhere.

And the most major problem — wrong names for mocks. foo, not foo.js (or reverse). Or ../foo, not ../../foo. You have to guest. You had.

Full API list (better refer to README or d.ts file)

— .enable/disable() — to enable or disable mock (enabled by default).
— .with() — overloads module with a value
— .withDefault() — overload `default` es6 export
— .by(otherModule) — overload by another module(if string provider) or by result of a function call.
— .callThrough() — first load the original module, and next extend it by provided stub.
— .mockThrough([stubFactory]) — first load the original module, and then replaces all exports by stubs.
— .toBeUsed() — will throw if mock was NOT used
— .directChildOnly — will do mock only direct dependencies.
— .calledFromMock — will do mock only dependencies of mocked dependencies

Plus plugin system and default configuration support, to let you tune rewiremock for your needs.

You can get a better proxyquire, you can fix mockery, you can use “Jest” mocks, you can test your mocks, predict mocking, prevent mocking.

Plugins includes naming aliases support, more deeper node.js integration, ability to mock in webpack environment, and many more.

Rewiremock v3.7.0. A year under development.

It is time to revisit your behavior and try a new tool, instead of the old one.

Lets make SkyNet great again!

SWEET!

Have a question? Reach me on Twitter!

Proxyquire is x200 times more popular, but it is a lemon tool! You might be interested in others rewiremock-related articles, where I have explained why #rewiremock is just... better.

--

--