Before we deep more into
Angular lands, I want to talk about spies. No, no that kind of spies.
When you are doing unit testing, you don’t want to leave your
SUT (subject under test) domain. If you’re testing a controller in Angular and it injects 3 services, you don’t care about those services, you only want to test that controller but also make sure it uses the services as intended.
That means that if your controller calls a service when doing something, you only want to know that the service was called but without actually using the service. Uhm, I am confused, how can I test that I called a service without using it? And what if I need what it was supposed to return to the controller? And and and…
Jasmine has a concept called
spies. You can
spy functions and then you will be able to assert a couple of things about it. You can check if it was called, what parameters it had, if it returned something or even how many times it was called!
Spies are highly useful when writing tests, so I am going to explain how to use the most common of them here.
For the tutorial sake, we are going to use a little snippet I created earlier: (Remember you can follow along using this plunk)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
We have here our
SUT which is a
Post constructor. It uses a
RestService to fetch its stuff. Our
Post will delegate all the
Rest work to the
RestService which will be initialized when we create a new
Post object. Let’s start testing it step by step:
1 2 3 4 5 6 7 8
Nothing new here. Since we are going to need both instances in every test, we put the initialization on a
beforeEach so we will have a new instance every time.
Post creation, we initialize the
RestService. We want to test that, how can we do that?:
1 2 3 4 5
We want to make sure that
rest is being called when we create a new
Post object. For that we use the jasmine
spyOn function. The first parameter is the object we want to put the spy and the second parameter is a string which represent the function to spy. In this case we want to spy the function
'init' on the
spy object. Then we just need to create a new
Post object that will call that
init function. The final part is to assert that
rest.init have been called. Easy right? Something important here is that the when you spy a function, the real function is never called. So here
rest.init doesn’t actually run.
Wait second, we already created the new
Post object in the
beforeEach! That is true, but here is an important concept: You can’t call a function, then spy it and assert it. Since on the
beforeEach we already called the
init function, the spy won’t work, that is why we recreate a new object for the test sake. You can also create the spy on the
beforeEach but since we just need it once, we do it on the
Our next test is about retrieving the data from the
RestService. Since it doesn’t return anything, what we could do is to make sure that the
getAll function have been called and also that our
this.posts contains what
getAll returned. Wait a second. Does the spy return stuff? Sure!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
We create an array of fake posts and then we call
spyOn on the
getAll function. We can make an spy returns something when called using
and.returnValue, which is called with our fake posts.
Having our spy in place, all what we need to do is to call
post.retrieve and then assert that
getAll was called and that also
post.posts contain our fake posts that were returned by
We now want to test our
accept function. We know that it will send a post to be updated on the
RestService so we need to be sure that the post was sent as a parameter.
1 2 3 4 5 6 7 8 9
We start with the fake post we want to update and then we create the spy. What is that
and.callThrough? Well, as you know, the real function is never called, but if you really need to spy it and also call it, you can do it with
and.callThrough. To see this working, you can check how
rest.update logs a message on the dev console.
Note: There is no real reason to use
and.callThrough here, I did it to show how it works.
After that, we just call
post.accept passing our fake post and then assert that
rest.update was indeed called with fake post as a parameter.
If you remember, we had the option to pass a callback to the
accept function. Let’s test that:
1 2 3 4 5 6 7 8 9
See how we created a
spy this time. We needed a function to pass to the
accept function as a callback method so we could perfectly create an object with a function and spy it as we used to, but we can create a spied function from scratch using
createSpy. After that, we pass it to
accept and as always, we assert that it was called.
Angular spies are heavily used on any kind of unit test. When we are testing something, we don’t care about the injected dependencies, we just need to make sure that they were used properly.
Spies are not the only solution and in fact, angular offer advanced ways to do this, ways I am going to explain later on this blog.
If you were lazy enough to not replicate this on a plunk, here it is completed