Before we start, I am aware of the following:
substitute.js
) among many existing ones.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.
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:
substitute.js
syntax is easier to remember, easier to learn and faster to write.With ts-mockito
you first create a mock “class” or “constructor”, and you can then create a real non-fake instance from this mock.
The same goes for typemoq
.
For substitute.js
, the instance and the fake is one and the same.
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:
This is unfortunately not supported.
The same goes for typemoq
— not supported.
In substitute.js
, you can fake from an interface just fine. The magic behind this is possible due to ES6 proxies.
In this example we’ll compare how the three frameworks handle stubbing of methods.
With ts-mockito
you specify the arguments and return values on the mock object within wrapper functions.
The same goes for typemoq
.
For substitute.js
, you can define your return values on the mock itself and yet use it in a fluent and strong typed manner.
In this example we’ll compare how the three frameworks handle stubbing of properties.
With ts-mockito
you specify the return values on the mock object within wrapper functions.
The same goes for typemoq
.
For substitute.js
, you can define your return values on the mock itself and yet use it in a fluent and strong typed manner.
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.
With ts-mockito
you specify the exception to throw on the mock object within wrapper functions.
The same goes for typemoq
.
For substitute.js
, you use the returns
function and wrap it in a lambda to throw an error.
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.
With ts-mockito
you specify the operation to match on the mock object within wrapper functions.
The same goes for typemoq
.
For substitute.js
, you use the received
function and call the operation on its return value.
Alternatively, you can also use didNotReceive
which does exactly what you would expect.
Proxying calls can be useful if you want your fake method to do something else.
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.
The same goes for typemoq
.
For substitute.js
, you use the mimicks
function and specify which function to proxy calls to.
Argument matchers allow you to be more explicit about when you want to fake something, or what calls you expect to receive.
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.
Almost the same as ts-mockito
, but supports lambdas.
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.
Proxying calls can be useful if you want your fake method to do something else.
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).
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)
For substitute.js
, you use the mimicks
function 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]
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 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.
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.
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