Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / DevOps / testing

Angular Testing Part 2: Jasmine Syntax

5.00/5 (2 votes)
7 Jan 2016CPOL4 min read 7.8K  
Angular Testing Part 2: Jasmine Syntax

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 until 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
JavaScript
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:

JavaScript
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.

JavaScript
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.

JavaScript
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:

JavaScript
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, you'll get a printable PDF cheat sheet full of Jasmine matchers and spy syntax (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.

JavaScript
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.

JavaScript
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 situations in Angular: how to test controllers, service, and directives… how to deal with promises… mocking services, spying on function calls, lots of fun stuff.

To be sure you don’t miss it, sign up for my newsletter over on my site. Thanks for reading!

Angular Testing Part 2: Jasmine Syntax was originally published by Dave Ceddia at Angularity on January 03, 2016.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)