Angular Testing Part 2: Jasmine Syntax

Dave Ceddia bio photo By Dave Ceddia Comment

In Part 1 of this series we looked at how to set up Karma and Jasmine, and wrote our first test.

If you haven’t done much or any testing up til now, Jasmine’s syntax can look a little strange. There’s nested describe, it, beforeEach blocks, and those expect matchers…

And then Angular heaps more syntax on top of that!

In order to get confident and fast at writing tests in your own app, it’ll help to have an overview of these functions.

You don’t have to memorize them all immediately – look them up when you need them – but you’ll probably find over time that you’ll naturally start to remember them all as you use them more.

Here are the ones you’ll use most often:

Jasmine functions

Jasmine’s core functions describe and it make up the heart of your tests. They’re meant to read line a sentence – describe("isUserLoggedIn") ... it("should return true when the user is logged in").

Sometimes adhering to this sentence-structure idea works easily, and other times it gets in the way. Don’t worry about it too much.

describe
describe("object name or feature", function() {
  // tests go in here
});

describe wraps a block of related tests. It takes a descriptive name, and a function that executes when your tests run.

It’s common to put the name of the object or function you’re testing, like describe("userService"). The describe blocks can be nested, too – for instance, your userService could have “logged in” and “logged out” states:

describe("userService", function() {
  describe("when logged in", function() {
    // test the features for logged-in users
  });
  describe("when logged out", function() {
    // test the features for everyone else
  });
});
beforeEach

beforeEach sets up preconditions, and will run before each and every test in its block. It takes a function, and is meant to be used inside describe blocks – it should be a direct child of a describe.

This is the place where you’d create or re-initialize any objects that you need to test.

describe("a counter", function() {
  var counter;
  beforeEach(function() {
    counter = 0;
  });

  // tests go here
});
it

it creates a test. It’s meant to be read as a sentence, as in it("should increment by one", ...). it takes a descriptive name and a function to run, and it should be nested as a direct child of a describe block.

The test count that Karma displays when you run karma start is based on how many it blocks you have.

describe("a counter", function() {
  var counter;
  beforeEach(function() {
    counter = 0;
  });

  it("should increment by one", function() {
    counter++;
    // now we need to verify it worked...
  });
});
expect

expect is a Jasmine expectation, and is meant to be used inside an it block. It allows you to make assertions. If any assertions in a test fail, the test will fail. If a test has no assertions in it, it will pass automatically.

It’s generally a good idea to have one assertion per test. In other words, one expect inside each it block. If you find yourself adding lots of expectations (assertions) to a single test, you might want to break that test up into a few tests.

That said, sometimes you want to check the value of something before AND after, to make sure it changed. Breaking the “rule” of one-assertion-per-test is fine in those cases.

Here’s that counter example again:

describe("a counter", function() {
  var counter;
  beforeEach(function() {
    counter = 0;
  });

  it("should increment by one", function() {
    // It's fairly unnecessary in this case, but in more
    // complex tests, a pre-assertion might be warranted:
    // expect(counter).toEqual(0);

    counter++;
    expect(counter).toEqual(1);
  });
});
.toEqual

.toEqual is a Jasmine matcher. There are a bunch of built-in ones, covering strings, object equality, and regular expressions, to name a few. If you sign up for my newsletter below, you’ll get a printable PDF cheat sheet with all the Jasmine matchers and spy functions too. (We’ll cover spies later on)

The matchers are chained off the expect() call, as in the example above.

Angular test functions

There are a couple functions you’ll need to use to test your Angular code. These are provided by the angular-mocks module (as we saw in the last post).

module

module loads an Angular module by name. If you need to load multiple modules, you can have multiple beforeEach(module(...)) lines. (But if you’re loading multiple modules, you might be testing too much at once.)

It’s generally used inside a beforeEach. Notice that you don’t have to specify a function – module returns one.

describe("userService", function() {
  beforeEach(module("myapp.services.user"));
});
inject

inject wraps a function that will get injected by Angular’s dependency injector. It works the same as with any other injectable object in Angular, but it has the added feature where you can optionally surround arguments with underscores, and it will inject them properly. This is handy, because you can name your variables the same as your services without naming conflicts.

describe("userService", function() {
  var userService;
  beforeEach(inject(function(_userService_, $rootScope, $q) {
    userService = _userService_;
  }));

  // userService is ready to test
});

What’s Next?

Now you’ve got a good understanding of the building blocks of an Angular test. The best way to learn these concepts is to practice them. Try writing some tests for your own app.

In Part 3, we’ll look at Testing Recipes that you can apply to different sitations in Angular: how to test controllers, service, and directives… how to deal with promises… lots of fun stuff.

comments powered by Disqus