Undersanding spyOnOne 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 EventsA 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.
- trigger event
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 agreeThe 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.