Next lesson playing in 5 seconds

  • Overview
  • Transcript

4.1 Unit Testing

Any non-trivial JavaScript application needs a healthy test suite, so that developers have the confidence to refactor without breaking things. In this lesson I'll show you how to get Karma and Jasmine up and running for unit testing with Gulp.

4.1 Unit Testing

Hi, folks. In this section, we're going to look at unit testing. We’ll need a number of new packages to get unit testing set up in the project. We'll need Karma and Jasmine as well as some additional plugins. Unit testing is important to ensure that the smallest units of our application work in predictable ways that we understand. We'll also need to install the Karma CLI, but this will need to be done globally. And you also need to make sure you have Python 2 installed on your machine and available on your system path, and it has to be one of the 2.x versions, version 3 doesn't work. Okay, so let's create some new directories. We'll need a test directory. And then, inside this, let's add a directory called unit. So we should CD into this new unit directory. And we can initialize the Karma test runner a using the Karma inits command. When we run this command, we need to specify the name of the configuration file that we want to generate. So this is very similar to the MPM init command that we ran right at the start of the course to generate our package.json file. This time, we're generating the configuration file for Karma. And again, we'll have a series of prompts, and we just need to answer the questions. So the first question is which testing framework do we want to use? And it's Karma-Jasmine. And that's fine, so let's just press Enter. And we don't wanna use require.js today, that's fine. And this time we want to change the default value. The default value is Chrome. Let's change that to PhantomJS. And we just need to tab until we see PhantomJS. And we can specify more than one browser. So, after we've hit Enter the first time, we see an empty prompt. And we just need to hit Enter again. We're only gonna use PhantomJS today. So, we need to specify the locations of the source and test files relative to the directory that we're currently in. So, the source files are at the path src.js. So, let's add that path. So, I just said that we need to specify these paths relative to the current directory. What we're gonna do once we've generated the configuration file is set the base path to be the root of the project. So, from the root of our project, the path that we've just typed is the path to the JavaScript files in the SRC folder. So, there are no files currently matching this pattern, that's because we haven't set the base path yet. So let's just ignore that and carry on. So we need to specify the test files now. So, all of our test files are going to end in .spec.js. And they'll all be in a folder called js inside the test unit folder. So now, we can specify any files that should be excluded. So, let's exclude the lint test and style test files because they were just test files and they had errors in them, or at least one of them does. And we can probably exclude the built files as well. So we don't want Karma to watch all of the files and run the tests on change. And our configuration file should now have been generated. Okay, so I said we were gonna set the base path. So, let's set that now quickly. And currently, the base path will be the unit directory because that's where the configuration file is. So we just need to navigate up to the root of the project. We also want to make sure that we can debug our tests if we need to. We'll set it up so that we can pass the flag --debug on the command line when we run tests. So, at the top of the file, let's first add a couple of variables to store some settings in. So we've added variables for source pre-processors, source reporters, and source single run. There are some options in this configuration file called pre-processors, reporters, and single run. And we're going to use the values of these variables instead of hardcoded values for those. But before we do that, we just need to add a small function that checks whether the debug argument has been passed. And this is going to be passed an argument. We'll just call it argument. And we just want to return with a argument is equal to --debug. So next, we can check the array of nodes global process objects. And when we are debugging, we don't want to have any coverage results. So, first of all, we can set the source pre-processor's variable to an empty array. And next, we just need to remove the coverage item from the source reporter's array. And going forward, if you use any additional reporters, you just always need to make sure that coverage is the last reporter because we're not going to do any complex checking of the values inside this array. We're just going to use pop to get rid of the last one. So as long as coverage is always the last item in the source reporters array, this should always continue to work. And we can also set the source single run variable to false. So now, let's use the new variables in the appropriate places. So we need to tell Karma which files we want to pre-process. And in this case, that will be JavaScript files. So we'll check out debugging shortly. So now, let's set up the Gulp task. There isn't a gulp-karma plug in. There used to be, but it's deprecated because it's easy to use Karma directly without needing a special gulp wrapper for it. So, we'll to require the Karma server in our task. And the Karma server is available under the server property of the Karma module. And all we need to do is create a new server instance, tell Karma where our configuration file is, and start the server. So we create a new server using server as a constructor. So, we add the new keyword before it, and we pass it a configuration file as the first argument. And within that configuration object, we specify where the configuration file, the unit.conf.js file that we just generated is. We do that using the config file property. We can use nodes__dirname global to get an absolute path to the current directory which will be the gulp tasks directory. So, we just need to add that to the relative path from gulp tasks to the unit.conf.js file. And we just need to start the server now, and we can do that with the start method. So, that should be all of the configuration that we need. Let's add a basic test file. So, that should be able to configuration we need. We should now be able to run some tests. So, I've got some example code that we can test. And I have some example tests. I'm not going to write the tests out from scratch. But let's just open them up and take a quick look. We'll put them in the correct location first. So, there's a file code emitter.spec.js. And a file called emitter.js. The emitter file is a very simple event emitter, so we'll put that into the src.js folder. And the tests we can put into the test unit directory. We're gonna put that into a folder called js, because it should mirror the contents of the src folder, where the code that's being tested lives. And we'll put the spec file directly in there. So let's just open up both of these files. So we've got a simple emitter class. It has a subscribe method and an emits method. And when you subscribe to a custom event, it returns back an object that has a sub method that can be used to destroy the subscription. And let's just take a look at the tests now for this. Jasmine has a describe method which is used to create test suites. And it has in its method which allows us to create individual tests. So, both of these methods take strings and functions as their arguments. The argument passed to the describe method should be a string that tells you what the test suite is about. And we then pass a callback function. And then, inside the callback function for a describe, we can add our tests, and we do that using the it method. And the first argument there is a string that describes the test. And the second argument, again, is a callback function. And then, inside that callback function, we can then implement the test. As well as the describe and it methods, we can also use before each, which is used to set up any state. And in this case, what we do is just create a new instance of the emitter class. So, let's look at one of the tests in a bit more detail now. So it allows callbacks to be registered. And inside this test, we use the subscribe method of the emitter. And that method takes the name of the event that we want to subscribe to as the first argument, and the function that we want to be subscribed as the second. So, in that second argument, we can update a local variable. And then, once we've subscribed, we can use the classes emit method to emit the custom event, and that should cause our local variable to be updated. So, following that, we use Jasmine's expect method, and the matcher to equal. And we pass the results that we want to test to the expect method, and then we use the matcher to say what we expect that value to be. So, in this case, we expect the result to equal true because the callback function that we passed to the subscribe method should set that value to true when the custom event is emitted. So that's an example test. There's a few of them in this file, and we should now be in a position to run these tests. And as you can see, it has run three tests. There are three tests in our spec file. And they've all passed, fantastic. So, one of the packages that we installed at the start of the lesson was the karma-coverage plugin. We can enable this purely through configuration. We just need to edit the unit.conf.js file once again. So, we already set up adding the coverage pre-processor and reporter at the start of the lesson, so we just need to add to the configuration for the coverage plugin. And let's do that, down here. We can add it after the reporter property because it's the coverage reporter that we configuring. So, let's add a HTML report. And we need to specify the directory that that report should get saved in. So, we can put that into a directory inside the unit folder called Coverage. So now, let's run the tests again. And we see the tests pass again, and we should now have a coverage report. And inside the directory coverage, there should be a coverage report for each browser that ran. In this case, it's in the PhantomJS. So, inside the PhantomJS folder, there should be an index page. Let's just open that in our browser. And we see a lovely report that tells us how much of the file being tested is covered. So, let's open the file up itself. And we have information here on exactly what is and what is not being tested. So, we can see that this line of the file is not being tested. And we can also see that only the first branch of the if is being tested. The else branch is not being tested. And that's the same for all of our if statements, and that's why the branches coverage is quite low. So, one thing we need to look at now is debugging. And we set that up so that we could pass the debug flag from the command line. So let's see what happens when we do that. So, this time the process starts, but it doesn't end. And what it does is give us this URL here of the Karma server. So let's just copy that. And we can open that in a browser, ad that will take us to the server page. And we need to hit this big deeper button in the over in the right here. So when we do that, it will load the actual test file. We won't see anything, but if we go into the console, then we can look at the sources. So, we can see the emitter class here, and we can add breakpoints. So, we just need to refresh this file, and it will load the file again, and it will hit our break point. So, if we want to see what's being passed, and I probably should have added that a bit lower down. Yeah, this time, we can see what the event name was and what the callback function was. So, debugging is really, really useful. So, in this lesson and we saw how to install Karma along with the Jasmine coverage and PhantomJS launcher plugins. We saw how to edit the configuration file, and customized it to allow us to debug our tests, if necessary, using the debug flag. Thanks for watching.

Back to the top