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
beforeEach blocks, and those
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’s core functions
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 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 blocks can be nested, too – for instance, your
userService could have “logged in” and “logged out” states:
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
This is the place where you’d create or re-initialize any objects that you need to test.
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
The test count that Karma displays when you run
karma start is based on how many
it blocks you have.
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:
.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 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.
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.
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, read my book Pure React.