3.5 Testing Angular Directives
Angular directives are one of the most powerful components of Angular, and one of the most difficult to test. In this video, we’ll combine everything we learned in the previous videos to test a simple but useful directive.
1.Introduction3 lessons, 07:24
2.Scaffolding a Testable Angular App5 lessons, 46:23
3.Testing Angular Applications5 lessons, 51:44
4.Code Coverage With Istanbul3 lessons, 18:01
5.End-to-End Testing With Protractor4 lessons, 23:27
6.Adding Finishing Touches4 lessons, 23:06
7.Conclusion1 lesson, 04:05
3.5 Testing Angular Directives
Welcome back, Madame and Monsieur. In this video, the final video of this section, we're gonna be testing directives. Now this is naturally very cool because directives are often touted as angular's coolest, most powerful feature. I intend to agree but just how do you test these elusive mysterious directives. It's not so hard so let's give it a shot. Let's go to our main.js file. And let's add a directive. This is a simple directive that is just going to display the first letter of a contact's name, sort of like a Gravatar. And we'll call this directive avatar. And directives return an object. We're going to restrict this directive to attributes or elements. As this is the most common configuration. We're going to pass it a definite scope, which means that whatever the values are, are going to have to be passed in from whatever the parent of this directive is. It's not going to pick it up automatically which is want we want. It's much easier to test elements with definite scope. So we'll say the property name on this scope must be defined explicitly in the parent. And last, we'll give it a template. We'll just make it a span. We'll give the span a class of avatar. And inside we'll take the first letter of the name. And we'll implement the proper filter we have. So we'll take the first letter of the name and we'll use proper to make it capital. We can now implement proper confidently because we've tested it. We know proper works so we're not introducing any unnecessary error into the rest of our code. We'll that's more or less it. So let's test it out. Let's go to our index.html for our app. And inside here, under contact.name, let's add the avatar. We'll just add a block called avatar. And we'll make name equal to contact.name. Let's serve it up and see how it looks. It looks like it's returning the whole name, not just the first letter. Let's go to our avatar directive and change this from name to name the first letter. And refresh this. And now we're seeing our avatars. The avatar for Robert is R and the avatar for Eddard is E. Precisely as expected. But how should we test this difficult to test thing. Let's go back to our main.spec.js. We'll add yet another describe block, our final describe block to describe this directive. We'll describe the avatar. Let's add a simple before each block where all we do is inject the module. Now our avatar only does one thing, which is display the first letter of the contact's name capitalized. [BLANK AUDIO] Now the way we test directive is a bit different. We're going to test the whole thing in our inject block. So let's add an inject block and we're going to need root sculp and compile. No injector this time. So to create a directive, you must create a scope for that directive and that scope will be used to kind of create the directive on a virtual level. In this case we're just going to use a rootScope which is a conveniently available scope for our purposes. We'll say rootScope.contact just like it was the scope of our contact controller in it's isolated ng repeater. And that's an object, and we'll just say the name is John Aaron. Next, we need to actually create and compile the element. Basically, we know that the directive is supposed to show a capital J, that's simple enough to figure out. But the way angular does it in reality is it puts it on the DOM, it checks all the DOM bindings, it runs watch and digest probably a million times, and then ultimately ends up with your directive. In this way, by using the compile function, we can virtually create a directive. So we'll say var elements, and we'll make that equal to $compile. And now we'll pass in a string, which is the exact string that the HTML would be if we were to type this directive, very similar to what we just created. We set avatar name equals contact.name. But where is contact going to come from? Well, compile returns a function and that function is going to take our rootScope. And what property does our rootScope have? Yes. A contact property. Now that this is all set up, we need to digest. Getting hungry, it's almost time for breakfast. So we can say rootScope.digest. This will basically move our app forward in time a small number of cycles, just enough for whatever was going to go inside that directive to go in. Now we can grab the actual compiled text from our directive. So we'll say var dirText equals element.text. And now we can run a test on it. We're expecting it to be j but let's start with a failing assertion. So I am gonna expect equal K. That won't be very likely. Let's run our test. Nothing is showing up. Could there be an error in our developer tools? There is. We're getting an error on line 78. Something to do with these closed quotes. As it turns out, we didn't close our quotes correctly in this block. So let's fix them up so all the quotes are closed as expected. All right, that's a lot of closing quotes. Amazing! So check this out. We're getting the very message we expected. It's not only running our test, and getting the right answer, but it's erroring to show us that it would error if it didn't work. What we see here is expected j to equal k. Awesome, that's what we expected. So, let's change this and we'll just expect it to equal j. And we'll save that and run our test again. Yeah. So, we now know that our avatar directive is correctly displaying the character, at least in the one test case we gave it. In a bigger app, we could write more devious test cases, like create a long list of names, and use a for loop to rapidly check them all. I really like doing this. The only reason we haven't done it in this tutorial series is in the interest of time. We've already spent a lot of time and we haven't even gotten in to Istanbul of Protractor, but boy howdy are those gonna be fun. But I hope you stick with me for the rest of this tutorial series.