A mock is basically a lightweight object that imitates the API and (to a degree) the behavior of other objects. This is useful if you have an object A that interacts with object B, but you want to write a spec that only tests the implementation of A. I think this is generally a good idea, but I’m not here to discuss the pros and cons of integration tests. In any case it saves you the code and time needed to set up a ‘real’ object B, which can add up if B is part of a complex architecture.
Note: All code snippets (or variants of them) shown here can be found in this Jasmine suite . It’s Jasmine 1.3 and 2.0 compatible and also has some additional examples/tricks.
Creating a Mock
Jasmine has something approximating mocks: ‘spy objects’. The
jasmine.createSpyObj method can be called with a list of names, and returns an object which consists only of spies of the given names. This is great, but maintaining a list of method names in your test code needs space, requires you to know the exact API of the mocked object, and can easily fail due to typos or API changes. In many cases you can avoid these issues with just a few extra lines:
To create a mock, give the constructor as the first argument, and a name as the second (optional). For example:
mock function admittedly has its limits, which we will discuss later. For now, let’s see what we can do with it.
Working with Mocks
The most obvious way to work with a mock is to use it as a parameter for methods and constructors. For example, we can easily test that a method sets the background of a given jQuery selection to red:
If your tested code is supposed to register event listeners on your mocked object, you can test these as well with another helper. This implementation assumes the API for registering listener is
object.addListener( type, listener ), but it can easily be adapted to most APIs that follow this pattern.
For Jasmine 1.3:
For Jasmine 2.0:
Now you can, for example, test if a change listener registered on your mocked object sets the event property “x” to 1:
When the object you want to mock is created internally in your tested code, we may have a bit of a problem:
That’s why I would recommend to use factories in your code:
By the way, the
mock function also works (for the most part – see next chapter) with native/DOM constructors. For example:
It is a common pattern to add methods to an object in its constructor to achieve some form of privacy, which prevents us from creating spies automatically:
The mock created above won’t have any spies – you will need to add them yourself afterwards or use
If the code you test uses the
instanceof operator, on your mock it won’t be identified correctly. Also, if you have “constants” (i.e. properties) on your prototype, they will not be available on the mock (they would actually have a spy in their place). These problems could be solved by a more sophisticated version of
This works fine…
… except when the prototype has native getters, which most DOM objects do. In such case, attempting to check the property type (or overwrite it) may throw an exception…
In the Jasmine Suite that accompanies this article I have outlined a third version of
mock, which at least supports the prototype properties, but it’s not an ideal solution. On the other hand, using
instanceof to check for anything but built-in constructors (e.g. Array, Function) is rarely useful in my opinion, so perhaps I just worry too much about this issue.
new calls, duck-typing instead of
instanceof). Personally, I have no problems adjusting my code here and there to be able to write better tests.
Of course mocks are not always the right choice. For static objects, Jasmine’s
spyOn functions should be used, and there is usually no point in mocking very simple objects that have no dependencies. And usually what’s important is that you use mocks at all, not how you create them – just using object literals can work fine too.