FREELessons: 2Length: 18 minutes

• Overview
• Transcript

1.2 Testing Angular Components

In this lesson we’ll add some basic unit tests for an existing Angular component using Jasmine, and then run them from the command line with Grunt and Karma.

To easily get an instance of the controller attached to the component, we can use the $componentController service. We can inject it into our tests using the inject method and save it to a variable. We wrap a service in underscores when injecting it, and Angular knows to unwrap it for us. See the code snippet below for an example. Code Snippet beforeEach(inject(function (_$rootScope_, _$componentController_) {$rootScope = _$rootScope_;$scope = $rootScope.$new();
$componentController = _$componentController_;

sandbox = sinon.sandbox.create();
}));


[MUSIC] Hi folks, in this course we're going to see how to unit test an angular component. First of all, we'll need to clone the project repository to our machine. We should make sure the command line or terminal is up and we're in the directory in which we'd like to clone the repo. I'm going to put mine in my user directory. To get the clean URL, you can go to the repository on GitHub. The repository is at https://github.com/danwellman/notes. And we can get the URL from the text input, just down here by the https box. We'll then need to move into the new directory using the CD, or change directory, command. We then need to get a specific branch within the repository which is called component-end. We can switch branches using the Git check out command. Once that's done, you should do a quick npm install, to get all of the project dependencies. Great, so, the repository contains a simple application called notes, which is used to create and store simple plaintext notes. Let's fire it up and just have a quick look. I'm using the development server built into the community edition of Visual Studio which is free to run the app. And the app is used to view or create or delete simple plain text notes. So we can click on a note in the sidebar here to view the note. And we can also create a new note if we want to. And once we've created a new note, we can then save it and we have to enter a title for the note. And once we saved it, it should then appear in the sidebar at the left, and if we decide we no longer need a note, we can then delete it. And we get this confirmation modal that asks if we're sure that we actually want to delete it, and we can either cancel that or choose OK, and the note will get deleted. So the component used by the app that we're going to be testing is for modals, and as you've seen, we have two modals. One of them is shown when we save a note and we have to enter the name for the note, and the other modal is shown to confirm we want to delete a note. So let's just take a quick look at the code for the component. So this is the component definition here and it contains a simple controller. And there is also a template file which contains the markup, or some of the mark up, the markup that isn't transcluded into the component as a HTML template. And there's also some styling which is added in the SCSS file and that gets compiled down into a CSS file. We won't be looking at either SASS or CSS in this course. The directory structure for the app is quite simple, as befits a small app such as this. We have a top level app directory and in here is all of the code for the app itself. We've got a JS folder with subdirectories for components, controllers, directives, filters, and services. And we've also got an SASS folder as I mentioned for the SASS. There is also a Templates folder for some of the markup and a Vendor folder for third party code. So, the Test directory is where we're gonna be working today. And inside here is a unit folder for all of the unit tests, and inside here we have an app folder. And the structure of the app folder should match the structure of the main app folder where all of the app code actually resides. And we've also got the Angular-Mox file which gives us some useful testing utilities and we've got a Karma configuration file, which controls how Karma runs our tests. So let's open up the app folder inside test/units. There's the JS folder, and here is a controllers folder and we do already have an example test, it's a very simple test. Let's just open it up and take a quick look. And we should actually be able to run this test from the command line, so let's just have a look at that. And we can run our tests with the grunt test command, I'm just going to clear the screen to tidy up the command line here. So now let's run our tests with the grunt test command, And we can see that our test has run, and it has passed. We can see that with the success keyword there, executed one of one success, and we can see how long our tests took to run. So let's go back to Visual Studio. First of all, we can rename the controllers folder, because we're not gonna be testing a controller we're gonna be testing a component. So let's rename the folder to components. And let's update the name of our test file. We're not testing the notes controller anymore, we're testing the modal component. Karma is set up so that it will automatically run any JS file inside the test/unit folder that ends in .spec.js. It's important to ensure that the filename does end with .spec.js otherwise the test won't run. So the first argument to the described method in our test is a description of the test suite. So let's just update that because it's not going to be a test test anymore, it's going to be a test for the modal component. So to test the modal component we first need to add some boiler plate for the test. We need to load the module that the component is attached to. We can do that at the top of the file right before the describe method that we've just updated. We use Jasmine's before each method to load the notes module. Next, inside the described method this time we can add some variables. Then we can use the beforeEach method again to inject the dependencies into the test. So the inject method is provided by the Angular-Mux file and we can use that to inject any dependencies that we need for the test. So we do need several dependencies, we need a root scope and the component controller, so let's inject those first of all. So we specify the dependencies that we want to inject wrapped in under scores, which Angular knows to unwrap inside the injector. So now let's store references to these dependencies in the variables that we created just a moment ago. We're going to need to create a new scope, so we can do that using the $new method of the rootScope. And we can also create a Xenon sandbox. So Xenon is already included in the project and configured to be able to be used with Jasmine. We can clean up after each test using Jasmine's afterEach method. And in here, we just want to reset the sandbox after each test runs. Great, so now we can write our first test. I like to treat the out to describe in a spec file as a test suite and have nested describe methods for each method of the component that is being tested. So let's add another describe method wrapping the its method. It checks to see whether that this event object has a target property. If it doesn't, it creates the target property and adds a class list object inside this, which has a contains method that just returns true. It then sets the modal is visible property to either true or false, depending on whether the contains method returns true or false. So let's go back to the test now and first of all, we can update the test description. So individual tests are added with Jasmine's it's method. So let's update the description there first of all. It's a little long for a description, but it describes what the test is doing in detail. It's probably not too long. To get the components controller we need to use the component controller service. So let's get rid of the expect here and let's use our modal variable, which we created back at the start. The componentController service is a method which we invoke in order to obtain the controller for a component. Using this service is useful, because we don't have to create fake DOM elements in order to get an instance of our components. The first argument is the name of the component the controller is attached to, and the second argument is the scope that we created earlier. We can also pass a third argument, which should be an object containing the bindings that we want to set for this instance of the modal. This is why we often initialize the component in each test, rather than in the before each for all tests. At this point, the modal is visible property should be undefined. So let's run the test now. And we can see that our test is still passing so the modal is visible property is currently undefined. We use Jasmine's expect method to make our expectation, and we pass this, the property that we want to check. And then we use the to be defined matcher, but the negated version with the .not matcher, changed before this method. And that just tells Jasmine that we expect our property not to be defined. So now we can invoke the hide modal method, and pass in an empty object, so an object that doesn't have a target property. And this time we can expect that the modal.modal is visible property to equal false. So we use the expect method once again and pass in the same property. But this time we use the two equal mantra because we're expecting the property to equal false. So let's go back and run our test again And we see that it's still passing, fantastic. We can also add a very similar test but which tests the opposite condition, So this time we'll need to create our own mock events object. We add targets and classless properties and a contains method that this time returns false which is the opposite of what it does in the actual file itself. So now we need to get an instance of our modal again using the componentController method, and that can be exactly the same as it was before. And we can invoke the hideModal method, this time passing in the mock event that we created And we can then make an expectation. So let's go back and run the tests again. And now we have two passing tests, fantastic. The component also attaches some event handlers for custom events when it is initialized. We can add a quick test for some of these as well. Let's use a new describe for this test. So now let's add the its methods This test is interesting because we need to add a spy, to spy on the on scope method, because the component uses this method to add the event handlers. We can create this by using the Xenon Sandbox and store it in a variable. So next we can initialize the component once again, but we'll also need to invoke the components onInit method, as the component controller doesn't do that for us automatically. Lastly, we can assert that the on method was called with the right arguments. So each time a method that's being spied on is invoked, the arguments that are passed to the method are saved. And we can access those using either the first call or last call methods of the spy, and the argument should be an array. So the component binds both to the show modal and hide modal events, so we check these. And the methods that it binds to these events are the show modal and hide modal methods. So we can also check that it's these functions that are being bound. So let's go back and run the tests one last time. And we can see that we have three passing tests, excellent. The project also has coverage setup, so let's run the index.html page which is insidetest/coverage/report.html And we can see that our component is quite well tested, but there are still some lines of code that are currently not being tested. It's not quite up to the level it should be, but that's all we have time for today. So as a follow up exercise, why not see if you can get the coverage up to 100% yourself using the techniques that you've learned today. So in this course we've learned how to unit test an angular component. We saw how to create a test suite and individual tests using Jasmine's describe and it methods, and we saw how to use the expect method to make assertions. We also saw how to use Angular's$componentController service to get an instance of the component that we want to test. If you get stuck and the tests aren't running for some reason, you can check out a different branch of the repository called component-tests, and that will have working code that you can compare against your own. I'm @danwellman on Twitter if you've got any feedback or questions. From all of us here at Tuts+, thanks for watching.