Set Up Tests for Kittenbook

There’s something about the testSum example that I really don’t like. I think the unit tests are good, and I think the example does a good job of demonstrating what types of components to include in a unit test. The problem is that testSum does a great job of teaching the basics of unit testing, but it doesn’t show you how to test real code. Unit tests for a sum function might not be hard to write, but how would you write tests for a real program such as kittenbook?

The first step is to think about the kittenbook code in units. The entire kittenbook project is a lot more complex than sum, but each unit (function) can be just as simple. In fact, this is one of the big advantages of writing unit tests: For code to be unit testable, the code must be written in small, manageable units. Code that is written in small, manageable units is cleaner, more organized, and generally less prone to error. Before we organized the kittenbook code into functions, it would have been almost impossible to test. We will not try to test all of kittenbook at once, but we will test each function in isolation.

The good news is that Grunt can help us with our unit tests. We will be using a testing library called Jasmine to write and run our unit tests. Jasmine basically provides testing utility functions such as test, plus a lot more. To get Jasmine set up, you first need to install grunt-contrib-jasmine (which installs Jasmine and all its dependencies). Then you need to add a Jasmine task to Gruntfile.js (see Listings 12.2 and 12.3).

Listing 12.2 Install grunt-contrib-jasmine from the Command Line


## First navigate to the kittenbook directory, then run the following command

~/project/kittenbook $ npm install grunt-contrib-jasmine --save-dev


Listing 12.3 Add a Jasmine Task to Gruntfile.js


module.exports = function(grunt) {
  grunt.initConfig({

    ...

    jasmine: {
      test: {
        src: ['js/values.js', 'js/prompt.js', 'js/getImages.js',
              'js/replaceImages.js', 'js/main.js'],
        options: {
          specs: 'test/*.js'
        }
      }
    },

    ...

  });

  // Load Grunt plugins

  ...

  grunt.loadNpmTasks('grunt-contrib-jasmine'),

  ...

};


Listing 12.3 shows only the new parts of Gruntfile.js. The new Jasmine task has familiar options. We tell Jasmine where to find the source code with the src attribute. I have listed the files in the same order as in the concat task because I found that this avoided some problems when trying to run the tests. Next, we tell Jasmine where to find the test files with the spec attribute (inside the options object). You need to create a new directory called test, which is where we will be putting all our test files. After you have done that, try running grunt jasmine from the kittenbook directory on the command line. You should get a warning message that says something like this: “Warning: No specs executed, is there a configuration error? Use --force to continue.” That means the Jasmine task is correctly set up, but Jasmine can’t find any tests to run. When you actually have a test to run, that warning will go away. So let’s write our first test for kittenbook.


Specs

Unit tests are often called specs (short for specifications) because they describe in detail what the software can and cannot do. In the last chapter, you learned about writing a specification as a part of planning. Writing unit tests is one good way to write a specification. If all the unit tests are passing, the software meets the specification.


I think functions with simple input and simple output are the easiest functions to test, so we will start with one of those: validatePhoneNumber takes a string as input and then outputs a Boolean. You can write several tests for a single function; each test should be testing one small feature of the function. Our first test checks whether validatePhoneNumber returns a Boolean, as shown in Listing 12.4. You can see a few of Jasmine’s utility functions as well. These functions are here to help, so try not to feel intimidated or confused.

Image The describe function describes what is being tested. describe accepts two arguments: a string that describes what is being tested and a function that contains the tests being described. In Listing 12.4, you can see a describe inside a describe, which is perfectly okay. The outer describe says that we are testing functions found in prompt.js, and the inner describe says that we are testing the validatePhoneNumber function.

Image The it function runs a single test. I would have picked a more descriptive name (such as test), but no one asked me. it accepts two arguments: a string describing the single test and a function containing the test code. The string usually starts with the word should, so the code reads “it should return a boolean.”

Image The expect function tests an expectation. If your expect passes, your test passes. If your expect fails, your test fails. If you don’t have an expect, your test always passes, so you should always have at least one expect within each it function. The expect is always accompanied by another utility function (such as toBe), so the expect line of code reads something like this: “Expect [actual result] to be [expected result].” If the actual result doesn’t meet your expectations, the test fails.

Listing 12.4 Test That validatePhoneNumber Returns a Boolean


describe('prompt.js', function() {

  describe('validatePhoneNumber', function() {

    it('should return a boolean', function() {
      var result = validatePhoneNumber('23456'),
      expect(typeof result).toBe('boolean'),
    });

  });
});


The test code in Listing 12.4 is describing the validatePhoneNumber function in the prompt.js file. Only one test has been written so far, and it states that validatePhoneNumber should return a Boolean. Within the test, validatePhoneNumber is called, and the value it returns is saved in the result variable. The next line says that we expect the (data) type of result to be a Boolean (typeof is an operator in JavaScript, which gives the data type of its operand). Note that we don’t care whether the result is true or false, as long as it is a Boolean. If the data type of result is a Boolean, the test passes; if it is not a Boolean, the test fails and we know that something is wrong with our application code. Run the tests from the command line using grunt jasmine, and you will see that our test fails. This is because validatePhoneNumber uses the String method match (which returns an array or null) instead of the Regular Expression method test (which returns a Boolean). Our unit test helped us find a bug in our code. After the bug is fixed, you should see something like Figure 12.3.

Image

Figure 12.3 The Jasmine output shows a satisfying green checkmark for each passing test.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.91.44