Angular Testing Part 2: Jasmine Syntax

By Dave Ceddia

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.

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.

Learning React can be a struggle — so many libraries and tools!
My advice? Ignore all of them :)
For a step-by-step approach, check out my Pure React workshop.

Pure React plant

Learn to think in React

  • 90+ screencast lessons
  • Full transcripts and closed captions
  • All the code from the lessons
  • Developer interviews
Start learning Pure React now

Dave Ceddia’s Pure React is a work of enormous clarity and depth. Hats off. I'm a React trainer in London and would thoroughly recommend this to all front end devs wanting to upskill or consolidate.

Alan Lavender
Alan Lavender
@lavenderlens