Not testing your Angular code? Here's how to start

Dave Ceddia bio photo By Dave Ceddia Comment

You know you should be testing your Angular code. But you aren’t.

It’s painful, too, seeing article after article espousing the need to test.

Sometimes you don’t even feel like a “real” developer. “Real developers write tests,” they say. “100% coverage is the only way to be sure.”

You’ve tried to test

Maybe you tried it once and ran into a roadblock testing directives.

Maybe you never even got that far – Karma and Jasmine and Grunt were just a pain to set up and you said, “Screw it I’ll do it later.”

It feels too late to start testing

Perhaps it’s worth giving testing another shot. But where to begin? The tutorials don’t cover how to test your code… and you can hardly just go on Reddit and admit to the world that you’ve never written a test. Those angry test-first people would have a feeding frenzy!

And there’s so much untested code already…

“All or nothing” is not the only way!

What if you could gradually introduce tests around your code, though? Little by little, the tests would form a scaffold of safety. Right away, you’d be able to refactor the tested parts of your app with complete confidence.

Sounds great, but how exactly do you test all the components in your app? The controllers, the services, and the trickiest of the bunch, directives? They’re all different.

What you need is a set of patterns – “recipes”. If it’s a service, test it this way. If it’s a directive, the test looks slightly different. Promises need their own special magic…

Part 1: Testing Environment and The First Test

To start with, we’ll set up a testing environment, and you’ll write your first test (or your first in a while!), in your own app, and start building that scaffold of safety.

In Part 2, we’ll cover Jasmine’s syntax.

And in Part 3, we’ll go over a few Recipes for testing the various parts of your app.

Watch the video for a quick walkthrough to get an Angular test environment up and running with Karma and Jasmine, and write that first test. (Or if video is not your thing, keep reading.)

Set up Karma

Karma is a test runner. Supplied with a configuration file, it will load up your tests along with your app and execute the tests in a browser of your choosing. The browser can be a real one (Chrome, Safari, Firefox, etc) or a headless one (PhantomJS).

Install Karma

Assuming you already have npm installed, this is easy:

npm install karma karma-jasmine karma-phantomjs-launcher phantomjs jasmine-core --save-dev
npm install -g karma-cli

What’s all this stuff?

  • karma - The Karma test runner.
  • jasmine-core - The Jasmine testing library that supplies the API for our tests.
  • karma-jasmine - A Karma plugin for Jasmine.
  • phantomjs - A headless browser to run the tests.
  • karma-phantomjs-launcher - A Karma plugin to start PhantomJS.
  • karma-cli - A command line tool, installed globally so that you can run karma from anywhere.

Configure Karma

Karma comes with a handy tool for getting started with a configuration. Run karma init and answer the questions, and it will generate a config file for you.

For the sake of this tutorial, answer:

  • jasmine for framework
  • no Require.js
  • PhantomJS for the browser
  • Put the paths to your source and test files. I used:
    • src/**/*.js
    • test/**/*.spec.js.
  • I didn’t exclude any files
  • yes to watch files

You’ll end up with a file similar to this:

Dependencies (Order Matters)

For the most part, this file can be used as-is, except for one section: the files to load. When your app is running in a browser, you’ve got index.html specifying all the dependencies. When it’s running under Karma, you’ve got this config file here.

So, you need to specify paths to your app source and test files (already done), and also any dependencies (UI Bootstrap, moment.js, lodash, etc). You also need to pull in angular and the not-so-obvious angular-mocks.

So open up that generated file, and make sure the files array includes everything you need, and in the right order. First angular, then angular-mocks, then your source and test files. Some dependencies (jquery) will probably need to go before angular, and other ones can go after angular-mocks.

You may need to npm install angular-mocks --save-dev if you don’t have node_modules/angular-mocks already.

If you get strange errors later (“Can’t find variable: whatever”), come back to this step and make sure you didn’t miss any dependencies.

Karma with Grunt or Gulp

If you use a build tool like Grunt or Gulp, you’ll probably want to integrate Karma with it. For Grunt, use grunt-karma. For Gulp, use gulp-karma. I won’t go into detail about setting these up, but leave a comment below if you want more help.

Write the first test

With Karma in place, you can write your first test!

Write a testable function

Pick a simple service or factory from your app. Add a new method to it called getGreeting that takes a name and returns "Hello (name)". Something like this:

angular.module('demo', [])
.factory('greeter', function() {
  return {
    // ...
    getGreeting: function(name) {
      return "Hello " + name;
    }
  };
});

You might be thinking this is awfully simple, and how will this apply to real code anyway. And you’re right, this is awfully simple.

However, it’s best to test out the pipeline with something we know will work. If Karma fails with some strange error, at least you can be pretty sure it’s not the test code.

Write the test

Create a new file called getGreeting.spec.js under the test directory (or wherever you configured Karma to load tests from). Type this in:

describe("getGreeting", function() {
  var greeter;
  beforeEach(module('demo'));
  beforeEach(inject(function(_greeter_) {
    greeter = _greeter_;
  }));

  it("says Hello to me", function() {
    expect(greeter.getGreeting("Dave")).toEqual("Hello Dave");
  });
});

Run the test

Back at the command line, run karma start.

Did you see PhantomJS 1.9.8 (...): Executed 1 of 1 SUCCESS? If so, nice work! You’ve got the base of your scaffold in place!

If something went wrong, it’s likely due to a missing dependency or syntax error. Go back to the dependency setup, and follow the stack trace if you got one.

What’s Next?

In Part 2 of this guide, we look at Jasmine syntax – the anatomy of a test. How do describe, it, and beforeEach work? When and how can they be nested? Those questions are all answered in Part 2!

In Part 3, we’ll look at the different ways to approach testing controllers, services, directives, and promises.

You’ll be able to start getting tests around your app, and start building that scaffold of safety! Sign up for my newsletter and you’ll get a Jasmine Cheat Sheet to help you on your way, as well as more Angular best practices and articles on Angular 2, ES6, and more.

Thanks for reading!

Want to get faster at testing? My Jasmine Cheat Sheet will help!

Sign up to get the cheat sheet and also get articles about best-practices Angular, ES6, Angular 2, and more.

A few emails per month — unsubscribe any time.
comments powered by Disqus