×
By the end of this chapter, you should be able to:
As you begin writing more complicated functions and larger applications, you're bound to make mistakes. Everyone does it, even professional programmers. When your programs grow they can become more difficult to reason about, and as hard as you may try it's impossible to predict every bug in your program. Fixing bugs also has a cost, as it can be quite easy for one fix to introduce bugs in other parts of your application.
Is there any way to avoid our programs becoming more brittle and difficult to maintain as they grow in complexity? Yes! The solution to this problem lies in testing our code as thoroughly as possible. In this chapter, you'll learn how to write tests in JavaScript that you can run automatically to verify that the code you're writing does what you expect. This makes it easier to protect against bugs, and to ensure that you don't introduce new bugs in your code as you add new features or rewrite old ones.
It might be difficult to appreciate the value of writing tests now, but it's a critical skill to have when you're working in a large codebase with a team of other developers. Are you down with TDD (Test Driven Development)? By the end of this chapter, hopefully you will be.
Before we see some code, let's define some technologies and functions we are going to be using.
jasmine
- this is our test runner and expectation/assertion library. We will be using it to run all of our tests. A test runner is a tool that is responsible for running tests that you write and logging the results of the tests for you to see. You can read more about it here. Jasmine lets you write tests so that they are quite straightforward to read.
When we write our tests, there are a few functions that we'll be using quite frequently. Here are three of the most important ones:
describe
- this function is given to us by jasmine
and it is what we use to organize our tests. You can think of a describe
function like talking to someone and telling them "let me describe ____ to you." Very often when you're writing unit tests, you'll have one describe
block per function you're testing (this will make more sense once you've seen some examples).
it
- this function lives inside of describe
functions. Inside of these it
functions we make our expectations. Each it
function corresponds to a test; if one of our expectations inside of the it
function isn't met, the test fails.
This might all sound a little strange, so before we get into a JavaScript-specific example, let's just look at an example written in plain old English. Here's how you might scaffold some tests to check whether a planet in our solar system is Earth:
expect
- this is a function given to us by jasmine
. When combined with describe
and it
, we can write tests that looks something like this:
Now that we have see this pseudo-code, let's look at some actual test code! Here's an example of test code written in JavaScript using jasmine
:
var earth = { isRound: true, numberFromSun: 3, density: 5.51 }; describe("Earth", function() { it("is round", function() { expect(earth.isRound).toEqual(true); }); it("is the third planet from the sun", function() { expect(earth.numberFromSun).toEqual(3); }); it("is the densest of all the planets", function() { expect(earth.density).toBeGreaterThan(5.5); }); });
Note the syntax here: both describe
and it
take a string as their first parameter, and a callback as the second. The callback to a describe
typically consists of several it
functions. Inside of each it
function is where we write our expectations.
Writing tests is all well and good, but how do we actually run these tests? If you google around, you will see that most of the tests we write are in the terminal. For now, though, we are going to be using the browser (jasmine.js, and jasmine.css) to run our tests.
To get started, we'll create a basic HTML page, and include the relevant JavaScript and CSS files:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Our First Jasmine Tests</title> <link href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.0.0/jasmine.css" link rel="stylesheet" /> </head> <body> <div id="jasmine"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.0.0/jasmine.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.0.0/jasmine-html.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.0.0/boot.js"></script> </body> </html>
Note that we include a div
with an id of jasmine
in the body. When we run the tests, jasmine
will display the results in this div; if you don't have an element with an id of jasmine
on the page, an error will show up when you try to run the tests.
Before including our test code, we'll also need to do a bit of setup for jasmine
. For now, let's just create a script
tag in our HTML and throw some JavaScript inside of it:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Our First Jasmine Tests</title> <link href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.0.0/jasmine.css" rel="stylesheet" /> </head> <body> <div id="jasmine"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.0.0/jasmine.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.0.0/jasmine-html.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.0.0/boot.js"></script> </body> </html>
Next, put the tests where the comment is, and refresh the page:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Our First Jasmine Tests</title> <link href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.0.0/jasmine.css" rel="stylesheet" /> </head> <body> <div id="jasmine"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.0.0/jasmine.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.0.0/jasmine-html.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.0.0/boot.js"></script> <script> var earth = { isRound: true, numberFromSun: 3, density: 5.51 }; describe("Earth", function(){ it("is round", function(){ expect(earth.isRound).toEqual(true); }); it("is the third planet from the sun", function(){ expect(earth.numberFromSun).toEqual(3); }); it("is the densest of all the planets", function(){ expect(earth.density).toBeGreaterThan(5.50); }); }); </script> </body> </html>
You can see what this looks like here! To see what failed tests look like, try setting earth.isRound
to false and running the tests again.
In practice, you won't be writing JavaScript inside of your HTML files. Instead, you'll typically have a couple of external JavaScript files. One of them is called a spec file (short for specification), and will contain all of your tests. The other will be the code that you're actually testing. So a more standard way to write the HTML might look like this:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Our First Jasmine Tests</title> <link href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.0.0/jasmine.css" rel="stylesheet" /> </head> <body> <div id="jasmine"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.0.0/jasmine.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.0.0/jasmine-html.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.0.0/boot.js"></script> <script src="your-external-script.js"></script> </body> </html>
When you're ready, move on to Testing Continued