In this post, I'm going to give you an introduction to test automation in Angular applications, as well as cover some basic theory concepts about testing.
TL;DR
In this post, I'm going to give you an introduction to test automation in Angular applications. First, I'm going to cover some basic theory concepts about testing, explain various testing levels (unit, integration, e2e, etc.) and touch on the importance of test automation metrics. Then I'll show you how to implement one version of these tests in a simple demo application.
Let's Answer Some Tough Questions
How many of you actually test your code? Don't worry; this is a rhetorical question, you don't need to raise your hands.
Well, if we're honest here, in my case (since I'm writing mostly JavaScript/Angular lately) up until recently, I was practicing a so-called CLT. Which, of course, stands for console.log testing.
We all know we should do something to make this better, but far too often, we do it like this gentleman here:
Ok, jokes aside, let me try to emphasize why testing may be useful to you. Just think about the following questions:
- Have you ever fixed a bug, only to find that it broke something in another part of the system?
- Have you ever been afraid to touch a complicated piece of code for fear that you might break?
- Have you ever found a piece of code that you're pretty sure wasn't being used anymore and should be deleted, but you left it there just in case?
Well, if the answer to any of these questions is yes, then you'll see value in what automated testing can bring to the table.
Why Bother with Tests?
So, why would you want to test your code in the first place? Isn't it enough that you have a deadline approaching, and now you should spend your precious time writing a test, instead of the actual application code?
Well, as features and codebases grow, manual QA becomes more expensive, time-consuming, and error-prone.
Say, for example, if you remove some function from the code, do you remember all of its potential side-effects? Probably not. But, if you have tests, you don't even have to. If you removed something that is a requirement somewhere else, that test would fail, and you'll know you did something wrong.
The cost of a bug that makes it into production is many times larger than the cost of a bug caught by an automated test. That means that automated tests give you a positive ROI (the investment is your time).
So basically, you should test your code to verify that it behaves as you expect it to. As a result of this process, you'll find you have better feature documentation for yourself and other developers on your team.
If nothing else can get you to see the value in testing, then keep this in mind:
Always code as if the person who ends up maintaining your code is a violent psychopath who knows where you live. 🤔
Types of Testing
Since most of us here are developers, we know what testing is. We open up the app, and we click through it, confirming that it works or not. Right? Wrong!
Here, I'm talking about automated tests, which run automatically.
Some of you may have heard about unit testing. However, that's not the only type of testing.
Unit tests, integration tests, and functional tests are all types of automated tests which are the basis of continuous delivery, which is a development methodology that allows you to safely ship changes to production automatically.
You shouldn't choose between unit tests, integration tests, and functional tests. Use all of them, and make sure you can run each type of test suite in isolation from the others.
- Unit tests
- ensure that individual components of the app work as expected. Assertions test the component API
- Integration tests
- ensure that component collaborations work as expected. Assertions may test component API, UI, or side-effects (such as database I/O, logging, etc.)
- Functional tests
- ensure that the app works as expected from the user's perspective. Assertions primarily test the user interface
- these tests are also sometimes referred to as End to end (E2E) tests or browser tests
For example, say you have a registration process in your app. A unit test may be written to ensure that the email validation function is working correctly.
If you want to test that a certain account was created with a corresponding record being added to the database, that would be an integration test.
Finally, the functional test would be when you would automate the clicking and email (and other relevant data) entering in the browser.
Code Coverage
As you'll get more into this field, you'll learn that it is a very useful information to know what percentage of your code is covered by tests. That is also known as Code coverage.
Code coverage is not the only test automation metric; I found a good blog post that actually notes 11 useful test automation metrics. So, you may want to revise this once you get a bit more into this field.
Knowing the data about certain test automation metrics can be crucial for improvement, as it helps you identify the parts of your system that need it.
Some organizations have their continuous delivery systems set so that they prevent the deployment if a certain percentage of code coverage is not met.
Demo Time
OK, enough with the theory, now let's see some code!
In this tutorial, we'll cover functional testing. You can check out this tutorial to see an example of using unit tests.
⚠️ As you'll see, this is great and all, so you may now be tempted to add them all over the place. If so, please read this post: Google's take on E2E, Integration and Unit tests. The gist of it is that you shouldn't overdo it with these type of tests.
Prerequisites
To be able to follow this tutorial, you need to have Node.js installed (version 6.9.0 or higher). Also, via npm, you'll need to install globally the following package:
Angular CLI is a command line interface to scaffold and build Angular apps. It provides you with a familiar project structure and handles all common tedious tasks (like generating a new service) out of the box.
Depending on your setup, you may need to install:
Protractor is an official library used for writing E2E tests in an Angular app. It's nothing but a wrapper over the Selenium WebDriverJS API that translates its methods to WebDriver JS methods.
Jasmine is a behavior-driven development framework for testing JavaScript code. It does not depend on any other JavaScript frameworks. It does not require a DOM. And it has a clean, obvious syntax so that you can easily write tests.
⚠️ What I would like to add here is that these days (especially in the JavaScript world), you have a vast number of options. Choosing one option and actually starting is way better than endlessly weighing the options.
With Jasmine, we'll be using a so-called Behaviour Driven Development (BDD)
style to write the tests. Tests are written in the following form:
- describe
[thing]
- it should
[do something]
The [thing]
can be a module, class, or a function. Jasmine includes built-in functions like describe()
and it()
to make writing in this style possible. Also, Jasmine offers some other cool stuff like spies, which we won't cover here, but you can learn more about it from the official documentation.
Generate a New Project
To start a new project with Angular CLI, execute the following command in your Terminal:
ng new AngularProtractor
Now go inside the newly created folder (cd AngularProtractor
) and run:
npm start
Now open up your browser and go to http://localhost:4200/.
You should see this:
Finally Let's Write Some Tests
At this point, we have everything set up, and we can start writing our tests. The beauty of using Angular CLI is that all the tools for testing get installed and setup for you.
Now open up the project in your editor and take note of the e2e folder.
You will see three files: tsconfig.e2e.json, app.po.ts, app.e2e-spec.ts.
In app.po.ts, we define an AppPage
class with methods (that we'll use in the test file):
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get('/');
}
getParagraphText() {
return element(by.css('app-root h1')).getText();
}
}
In app.e2e-spec.ts file, we first import AppPage
:
import { AppPage } from './app.po';
describe('angular-protractor App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('Welcome to app!');
});
});
When we're using Jasmine to test our code, we group our tests with what Jasmine calls a test suite
. We begin our test suite by calling Jasmine's global describe
function.
This function takes two parameters: a string and a function. The string
serves as a title, and the function is the code that implements our test.
Within this describe block, we'll add so-called specs. Within our it
block is where we put our expectations that test our code.
Don't worry about the syntax; that can be easily learned by going through Jasmine's documentation. And, besides, the good news is that all of the test tools have more or less similar syntax.
Run the Tests
To run the E2E test, execute:
ng e2e
Among other output, you should see:
Jasmine started
angular-protractor App
✓ should display welcome message
Executed 1 of 1 spec SUCCESS in 0.864 sec.
If you mess up the test, and put, for example:
it('should display welcome message', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('Testing failure');
});
You will get:
**************************************************
* Failures *
**************************************************
1) protractor-test App should display welcome message
- Expected 'Welcome to app!' to equal 'Testing failure'.
Executed 1 of 1 spec (1 FAILED) in 0.84 sec.
I'll list a few third party tools here that I found interesting. I'm not affiliated with any of these services; I just happen to like what they offer after doing my research:
I just want to say that every great tool has its price. And, if you're contemplating (or even complaining) about whether or not you should pay for certain software, please read this awesome post by Ambrose Little on How Much Is Your Productivity Worth?.
Conclusion
In this post, I gave you an introduction to test automation in Angular applications. I covered some basic theory concepts about testing and explained various types of testing like unit testing, integration testing, and E2E. Also, I touched on the importance of test automation metrics. Finally, I showed you how to implement E2E tests in a simple demo application.
I hope that this post showed the value of automated testing and that you'll continue your learning in this area and apply it to your workflow.
History
- 29th December, 2017: Initial version