Testing
The AngularJS framework encourages writing clean, solid, testable code. This feature is one of the most useful that you get out of the box with Angular.
The Angular team’s emphasis on testing is so strong that they built a test runner to make the process easier. In their words:
JavaScript is a dynamically typed language which comes with great power of expression, but it also comes with almost no help from the compiler. For this reason we feel very strongly that any code written in JavaScript needs to come with a strong set of tests. We have built many features into Angular which makes testing your Angular applications easy. So there is no excuse for not testing.
Why Test?
When building any non-trivial application for any business purpose beyond prototyping (and even then), it’s important to be confident about our code. When we have tests backing up our codebase, we can discretely know whether or not our code is working as intended.
Bugs in our code are inevitable, and without tests it’s difficult to know where they are; tests make it easy to isolate and eliminate them. They make it easy to onboard other developers and provide working documentation about the code.
Testing is essential if we are to understand what is happening in our app.
Testing Strategies
When building a testing suite to develop Angular apps, it’s always good to have a strategy for how and what we are going to test in our app. If we end up not testing anything of substance and write meaningless tests, we’ll have gained nothing to give us confidence our apps are functioning properly. Conversely, if we test everything we can possibly think of, we’ll end up spending more time writing tests and fixing minor bugs in our test code than we will on our app.
It’s important to be realistic about the value we’ll get out of the tests we do write and about what we should be testing.
At the end of the day, our tests are both a tool for us to gauge the health of our app and a measuring stick to tell us if we’ve broken our code when introducing new functionality.
Getting Started Testing
One of the major hurdles in getting started with testing is setting up a test runner that runs tests on our code. JavaScript testing is also a bit more difficult, because it requires us to build automation into browsers.
Building a development testing suite is difficult enough, but what about supporting continuous integration so that new deployments can be automated and we can be confident about the quality of the code before making a new one?
In software engineering, continuous integration is the practice of merging development working copies of a shared mainline several times a day and running the test suite upon update.
Karma is a testing tool that was built from the ground up to remove the burden of setting up testing and allow us to focus on building our core application logic.
Karma spawns a browser instance (or multiple different browser instances) and runs the tests against the different browser instances to see if the tests pass under different browser environments. Karma communicates with the browsers through socket.io, which enables Karma to keep in constant contact. Thus, Karma provides real-time feedback about what tests are running and gives us a human-readable output that tells us which tests pass and which ones fail or time out.
Karma is capable of communicating with several different browsers natively and removes the need for us to manually test our code in different browsers. For example, it can run the tests in Chrome, IE, and Firefox and spit out the results to your console. We can even hook up to our own native devices (yes, like an iPhone or iPad) to test our code.
Types of AngularJS Tests
There are several different ways to test our Angular apps, depending upon what level of granularity we want to focus on and what features we want to target.
Unit Testing
We can focus on building our tests to isolate specific, isolated components of our code. This approach is called “unit testing”, where we test specific units of code for all sorts of different input at different stages and under different conditions.
Unit testing is specifically for testing small, individual units of code, single functions, or small and contained function interactions. It is not about testing large feature sets.
The tricky part about unit testing is setting up the isolation of one piece of logic so that we can test it. We’ll discuss strategies of accomplishing this isolation later in this chapter.
When Is Unit Testing the Right Choice?
When we’re writing our functional code, we’re going to create little components of functionality. For instance, in building an application that handles live filtering elements in a list, we’re going to build the filtering functionality.
The feature of this filter is a ‘unit’ of functionality that is an ideal case where we’ll want to test with a unit test. To be confident that this filter functionality has been implemented and is working as expected, we’ll need to isolate the component and test it for different inputs.
Imagine we’re building a rocket ship. We’ll want to test each individual part of the ship (e.g., the thrusters, the joystick controls, the oxygen system) to verify the ship is generally working how we expect it to work.
E2E Testing
On the other hand, we can black-box test (i.e., perform an end-to-end test on) our application. In an end-to-end (or E2E) test, we test the application from the point of view where we are an end user and know nothing about the underlying components of the system. This method is great for testing large features of the application.
E2E testing works well for testing the user interaction with the page without forcing us to refresh the page manually and test with the browser.
This kind of testing is nothing new, and there are great tools that enable us to set up automated browser testing. We can use tools like PhantomJS or CasperJS for headless browser testing (i.e., without opening a browser) or tools like Karma that will open a real browser and perform all the tests in an iframe.
When Is End-to-End Testing the Right Choice?
When we’re writing tests of use-case functionality, it’s always a good idea to write a test to walk the path of our user. End-to-end testing is great because it maps out the real experiences our users will have when using our application.
For instance, when building a user login flow, we test that the user is logged in and redirected to his or her homepage. We don’t worry about how the user is logged in, just that they are logged in and directed to the proper place.
Imagine you’re building a rocket ship. End-to-end testing doesn’t care about the engines or the landing gear, it cares that the rocket takes off and flies your astronauts to space.
Both unit testing and E2E testing are supported out of the box with the Karma test runner.
Note that writing unit tests instead of E2E tests will allow our tests to run extremely quickly. Setting up our tests to run synchronously and using mocking libraries will greatly speed up our testing, as well.
Getting Started
In order to run our tests, we’ll need to install the Karma test runner. At this point in the book, you likely already have NodeJS and npm available to you; if you don’t, make sure you install them. Once you do, we’ll use the npm
command to install the Karma tool:
We’ll save our dependencies in the package.json
file. To set up the package.json
with npm installed, simply run npm init
and walk through the wizard.
To start testing our application, we’ll need to set up a reasonable structure for both our application code and our test code.
We recommend storing application files in the following format:
app/
index.html
js/
app.js
controllers.js
directives.js
services.js
filters.js
views/
home.html
dashboard.html
calendar.html
test/
karma-e2e.conf.js
karma.conf.js
lib/
angular-mocks.js
helpers.js
unit/
e2e/
The app/
layout is standard, wherein we divide our application code. The test/
directory nests our tests in the appropriate directories that reflect the type of test: unit/
or e2e/
.
There is yet still debate on the best structure for file types in the current version of Angular. This is one proposed layout for the test file structure.
There are two different Karma configuration files in the test/
directory. Each of these files contains the specific type of test that it will run. As we walk through each type of test, we’ll discuss how each Karma configuration should look and how to customize it for our use.
Running a Karma test is simple: karma start path/to/karma.config.js
. When the test runner starts up, it will start the browsers listed in the Karma config file.

By default, if not otherwise specified, Karma will watch all the files listed in the Karma configuration. Any time a file changes, Karma will run the appropriate tests.
Initializing Karma Config File
Karma gives us a generator to help us build configuration files. This generator will ask a few questions about how we want to set up our configuration. Each question suggests a default value so it is possible to simply accept all the default values, which we’ll do in a moment.

The process of setting up testing with unit tests and E2E tests is largely the same. We’ll use the karma init
generator to create karma.conf.js
configuration files.
Setting Up Unit Testing
First, let’s run the karma init
command with the path of our test file. In this case, we’ll build our Karma config in our tests directory:
$ karma init test/karma.conf.js
For unit testing, we need all of our dependencies to be available. When building our unit tests with the Karma generator, it’s important that our unit tests contain references to code for:
- a testing framework (choose one):
- Jasmine (default)
- Mocha
- QUnit
- custom test configuration (required w/ Mocha)
- any vendor-required code
- our app-specific code
- our test code
angular-mocks.js
library for mocking
Unit tests need references to all of the app code that we’ll be testing as well as all of the tests that we’ll be writing.
For instance, a sample unit test Karma config file might look like the following (comments removed for simplicity):
module.exports = function(config) {
config.set({
basePath: '..',
frameworks: ['jasmine'],
files: [
'lib/angular.js',
'lib/angular-route.js',
'test/lib/angular-mocks.js',
'js/**/*.js',
'test/unit/**/*.js'
],
exclude: [],
port: 8080,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Safari'],
singleRun: false
});
};
This config file is similar to the one we’ll generate.
Once this file is set, we can run our unit tests like so:
$ karma run test/karma.conf.js
Alternatively, if we want the tests to run any time the code changes (if we set autoWatch
to true):
$ karma start test/karma.conf.js
Setting Up E2E Testing
Ready to master AngularJS?
- What if you could master the entire framework – with solid foundations – in less time without beating your head against a wall? Imagine how quickly you could work if you knew the best practices and the best tools?
- Stop wasting your time searching and have everything you need to be productive in one, well-organized place, with complete examples to get your project up without needing to resort to endless hours of research.
- You will learn what you need to know to work professionally with ng-book: The Complete Book on AngularJS or get your money back.
Get it now