Saturday, April 20, 2013

Jasmine Tips: Spies!

We Started using Jasmine for JavaScript testing about 4 months ago. It was chosen because it resembles rspec, and lately minimizing context shift has been a goal for our team. So far I really like Jasmine. There are however, some cases that can be frustrating for someone new to unit tests with JavaScript or Jasmine.

Undersanding spyOn

One of the most important parts of Jasmine tests is understanding spies. I use spyOn because jasmine.spy is a long string to type. Spies can be confusing initially, but they are very useful. Their primary function is to check that a specific method is called, but they can also be used for stubbing.
What spy does is replace the argument with a function that tracks function calls. This means that the replaced function will not be called, the spy has taken its place.

This can be useful when testing objects that involve form submissions, it prevents a situation where your test browser keeps submitting and never finishes the suite.

andCallThrough()

You might want the method being spied on to be called as well. Sometimes not doing so will cause an exception. This is easy to do.

andReturn()

It is also possible to force the spy to return a specific value. This can be useful for stubbing external methods and testing how your code responds based on external conditions, withought going through the trouble of causing those changes. It also helps keep the unit tests isolated, which is very good.

andCallFake()

For testing callbacks it is possible to mock a response, and make sure the correct responses are triggered for different cases. The below example is a little long. But what is happen is that andCallFake is used to make sure that the correct callbacks are used to respond to save success and failure.

Checking Events

A common test I will write is to make sure that an event is getting fire when a method is called. For this you want to use a spy. I usually use spyOn for this. The important pattern to remember is.
  1. spy
  2. bind
  3. trigger event
  4. expect
The first step is to spy on the event you expect to be trigger. Next, if necessary, bind your spy to the event. Trigger that event. And then expect the spy to have been called.

Another option for the case above would be to spy on the model's trigger method. This has the benefit of terseness. It also requires testing the arguments. This can be done simply

Nick hates the word "Gotchas!", I agree

The spy, bind, trigger, expect pattern is simple enough. But when the object being spec'd is doing the binding the patter can be more difficult to grasp. Look at the below example.

This will fail, because spyOn replaces the reference to the function with a spy, but the original method has already been bound to the event. The pattern above is: bind, spy, trigger, expect. It will not work. I like to fix this by splitting event binding out of the initiaize method.

The above code will pass because the spy has been bound to the event. And you can see that it has taken on the form of spy, bind, trigger, expect.
In general it is a good do as little as possible directly in the constructor (initialize for backbone classes). This makes the code more testable. And writing testable code is good.

Conclusion

Spies are awesome. They are kinda a Swiss Army knife of Jasmine testing. Its a straightforward way of solving a lot problems associated with unit testing (stubbing, mocking, testing events). They can be tricky, but with a little experience they can be very useful.

No comments:

Post a Comment