JavaScript Unit Testing With Jasmine: Part 1

Now that you have an awesome testing environment with Testem set up, you need to actually start writing tests. This is where people begin to feel daunted. “Oh no, I need to learn another library” comes to many people’s minds and because this library doesn’t add any cool features to your site or application, it doesn’t seem like much fun. We need to brush that aside, though, because testing libraries tend to have very, very simple APIs and can be learned within a couple hours of tinkering. Let me prove it to you.

Suites and Specs

Jasmine has a few main global functions in its arsenal. These functions are global mostly so that the code is simpler to read. You can actually nearly read it like a sentence. The first function we’ll talk about is describe. This function is used to organize your tests into suites. A suite is just a fancy name for a collection of tests so that you can organize your tests into related blocks. Here’s how describe looks in action:

1
2
3
describe("A description or subject for your test suite", function(){
// ... Write your tests here.
});

As you can see it takes two arguments: A string that is used to identify a suite and a function that contains the actual test code. The string is used in the test runner to hierarchically display which tests passed and which ones failed. The function can contain any code you want it to contain; however, you need to use specific Jasmine-supplied functions in order to get anything to show up in the test results, as we’ll explain in the next section.

Suites can be nested inside of each other too. This allows you fine-grained organization of code into hierarchical structures. I usually have an initial describe block for an object and then have nested describe blocks for each of its methods, like so:

1
2
3
4
5
6
7
8
9
10
describe("Some Object or 'class'", function(){
// ... some tests
describe("#methodName", function(){
// ... tests related to this method
});

describe("#anotherMethod", function(){
// ... tests related to this method
});
});

Now that we have the code organized into suites, which generally represent the nouns, we need to write some tests (aka specs), which are the verbs. We do this with it. This is how it looks:

1
2
3
4
5
6
describe("This thing", function () {
it("is awesome", function () {
// ... Do a bit of setup
// ... Check to see if it really 'is awesome'
});
});

As you can see, it is nested within a describe block so that the spec’s verb can be associated with a noun that commits the verb. So, describe is where you say what the object is that does something and it is where you say what it does. Within it is where you actually test to see if the verb was completed successfully. We’ll discuss how to do that in the next section.

Before we take a look at that, though, take a close look at the comments I placed inside the it block. I separated it into two different sections: setup and checking. First you set up and run the necessary functions. Then you test to see if everything worked out the way it should have. This is the “standard” way of doing a test and is the pattern you should always follow. Obviously, though, if there’s nothing to set up, just skip that part and start checking.

What to expect When You’re Expecting

As I just said a moment ago, within it is where you do your actual testing. Essentially, you are just checking to see if the outcome is the same as you expected. Jasmine uses expect as the name of the function that is used to test the expected outcomes. expect takes a single argument, which can be any value, and then returns an object that has a bunch of methods called matchers. The matchers are what test the value to determine if it was correct. It’s hard to explain this without code, so take a look:

1
2
3
4
5
6
7
describe("This thing", function () {
it("is awesome", function () {
var returnValue = 1 + 1;

expect(returnValue).toBe(2);
});
});

We passed returnValue into expect and it gave us an object with the matcher methods on it. We chose to use the toBe matcher and passed in 2 as the argument. toBe just compares the value given to expect with the value given to it using ===. Essentially this is what is happening for Jasmine in this situation:

1
2
3
4
5
6
if ( returnValue === 2) {
// report the test passed
}
else {
// report the test failed
}

If you don’t use expect and a matcher, your tests will always be considered passing tests, unless there is an error thrown (there is at least one exception, which I’ll discuss later). If you really want to be sure your tests are passing/failing, then you need to use expect. You can use expect as many times as you want per it block, but you should try to keep them minimal. If you need to call expect a lot, it probably means that the functions you’re testing are doing too many things. If any of the expects fail, the entire spec will fail.

There are a ton of matchers, and it doesn’t really pay to go over them all here. toBe is a very common one and is definitely one of the easiest to understand and can be used in most situations, but you should see the Jasmine documentation for the rest of the matchers. You can also create custom matchers, but I won’t go over that here. They just allow you to write matchers that simplify how you write your tests so that they are more domain-specific.

Setup and Cleanup

Two more of the functions that Jasmine provides are beforeEach and afterEach. These aren’t necessary, but can help keep your code DRY. You use them inside of your describe blocks and before your it blocks. They each take a function as their sole parameter, and these functions are run before/after each of the specs, including the specs nested inside deeper describe blocks. This way, if you have some common setup or teardown procedures, you can place them inside one of these functions and only write it once instead of repeating it within each of your it blocks. Here’s an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
describe("This thing", function () {
beforeEach(function(){
// Run some setup, like creating new objects
});

afterEach(function(){
// Run some cleanup like disconnecting WebSockets
});

it("is awesome", function () {
// beforeEach is run before this
var returnValue = 1 + 1;

expect(returnValue).toBe(2);
// afterEach is run after this
});

it("makes 'cool' look like a hot summer day", function () {
// beforeEach is run before this
var returnValue = getSomeValue();

expect(returnValue).toBe(1);
// afterEach is run after this
});
});

What’s this?

The final thing we’ll be talking about today is the this keyword. You can of course use closures and local variables for containing all of your data that will be passed around to each of your functions, but this isn’t the only way. Each function you pass around (such as to beforeEach, it, and others) is run in the same context. This means that you can define something in beforeEach with this.someObj = … and you can access that object within it with this.someObj. It’s entirely up to you which technique you use, but I still wanted you to see your options so you have a choice.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
describe("Some Object", function () {
beforeEach(function(){
this.someObj = new SomeObject();
this.someObj.start();
});

afterEach(function(){
this.someObj.stop();
});

it("increments", function () {
this.someObj.increment();

expect(this.someObj.value).toBe(1);
});

describe("#aMethod", function(){
it("does stuff", function () {
var value = this.someObj.aMethod();

expect(value).toBe("returned string");
});
});
});

Conclusion

That’s all I’m going to talk about today. Next week we’ll discuss a few more things that Jasmine offers us, such as Spies, jasmine.any, and asynchronous testing. I hope that you are looking forward to it and spend a little time practicing your Jasmine unit testing before we get into it. God bless and happy coding!

Author: Joe Zimmerman

Author: Joe Zimmerman Joe Zimmerman has been doing web development ever since he found an HTML book on his dad's shelf when he was 12. Since then, JavaScript has grown in popularity and he has become passionate about it. He also loves to teach others though his blog and other popular blogs. When he's not writing code, he's spending time with his wife and children and leading them in God's Word.