In this lesson Trevor looks at testing AngularJS controllers with scope inheritance with Jasmine and test spies.
Trevor: Here's a simple app that demonstrates controller scope-based inheritance. I have child controllers here that run each one of these musicians, that can be up-voted or down-voted, and then a parent controller that keeps track of the total votes that the user has cast, and the last person they voted for. Let's take a quick look at the code here.
We can see that the election control is sitting up at top, and then the musician control is sitting right down below, right on the repeater. These are ordered by the number of votes.
In this case, they're going to use, and they are exhibiting, scope inheritance. If you haven't worked with that yet I recommend checking out Egghead Video number three, where John goes over the process of scope inheritance, particularly in using controllers.
If we take a look at the election controller, we instantiate the values, and then we have this "count vote" method. This is what's going to increment total votes, and set last votes. Musician controller has an "on vote" method, this is for down and up-votes, and it's also going to call the "count vote" method with the musician name.
In the election spec, we test our instantiation of values, and then we also test the "count vote" method, because that's defined in the election controller. This is important to keep this test here, because it actually is a method that lives up there.
In the case of the musician spec, it only tests on vote, even though it is the one calling "count vote." That interaction, that interdependency, is where our problem is going to occur, so let's run these tests real quick.
You can see we're getting a couple of errors here. We're going to get the "undefined is not a function" for "scope dot count vote," and it's going to be on the musician controller, as you can see right here.
The issue is, when this is instantiated in a test environment, this scope knows nothing of the parent it's supposed to inherit from, because it doesn't have the angular DOM environment to deal with that.
There's a lot of ways to do this process, but my preference is using Jasmine Spies, because it still cuts off the dependency from the two pieces. What we'll do is, we'll say, "scope dot count vote," that same method, it's going to be "Jasmine, create spy." There's a number of ways to create spies, but this is a fairly simple way to do it for our purposes.
This should do the job for us, because this is going to just create the spy. If we run these, it looks like we have seven tests passing.
We can actually go a step further though. We want to know that this has called it. It should call "scope dot count vote," the musician name when "up" is true, and then it should do the same when "up" is false. We're just testing both conditions, both cases, argument-wise. These are basically these two conditions right here.
We're going to use a new match here and it's called "have been called." We're going to say "scope dot count vote," which is our spy. To have been called with "Phil Collins," who is our musician here. We can also just do a blank "have been called," but it's nice to do it with the argument, because we're actually passing an argument in this case.
Let's take a look at that then. Great, we have nine specs passing. Now we've got coverage for musician calling this method, which is defined on the parent, yet we're not creating the dependency between the two. Which is the preference for testing, because then these things can change as they will, but the way they're called is still going to be tested in the according spec.