Poorly written code, buggy functionality, and bad refactoring practices can lead to unreliable applications. Writing good tests will help detect these types of problems and prevent them from negatively affecting your application. It’s vital that you thoroughly test your application if you want to make it sustainable and supportable for years to come. A core purpose of writing tests is to help guard against breaking application functionality when you have to add new features or make bug fixes later on.
If you’ve developed an Angular application, you may know that Angular is a great framework for building testable web and mobile web applications. One of the goals in writing Angular was to make it a testable framework, and it shows.
Although testing Angular applications is of utmost importance, figuring out how to do that has been challenging until now. You may have been able to find a blog post or two, perhaps a video, but generally materials have been lacking to help guide you through all the different aspects of testing in one place. Well, you’re in luck! In your hands (or on your screen), you hold the key to getting started with testing Angular applications.
This book will help you build a foundation for testing the most important parts of Angular applications with confidence. We assume that you have some familiarity with the Angular framework, TypeScript, and command-line tools. If you haven’t written a test, this book will teach you enough fundamentals to get you started.
If you don’t have experience with Angular, now is a great time to learn about the Angular applications. For newbies, we would encourage you to walk through the tutorials and introductory information you can find at https://angular.io.
In this first chapter, you’ll get an overview of testing Angular applications, take a brief look at TypeScript, learn about the testing tools you’ll use, and be introduced to unit and end-to-end (E2E) tests. Let's get started!
Most Angular testing you’ll find out there involves two types of tests: unit tests and E2E tests. The bulk of this book will revolve around those two types.
This book is separated into two parts. The first part covers unit testing, which tests units of code. You’ll learn how to create unit tests for components, directives, pipes, services, and routing—using testing tools like Karma and Jasmine—and run those tests using the Angular command-line interface (CLI). The following list breaks down each of the testable concepts we’ll cover in part 1 of the book:
ngFor
, ngIf
, and ngSh
ow
.In the second part of the book, we’ll dive into E2E testing using the Protractor framework. You’ll get practice writing tests that behave as if the interactions were coming from the user in a browser.
As for which version of Angular you’ll be using, this book is written to be compatible with versions of Angular 2 and later. Angular 2 was a complete rewrite from AngularJS 1.x, so that’s the base for the current version.
In the next section, we’ll look at TypeScript, which is the language you’ll use to write tests in this book.
TypeScript is an open source language created at Microsoft in 2012 by Anders Hejlsberg, who also created C#.1 The major problem that Hejlsberg attempted to solve with TypeScript is that JavaScript was never meant to be used with large-scale applications. The first version of JavaScript was created in 1995 in 10 days by Brendan Eich2 and was meant to be used as a scripting language for adding interactivity to web pages.
Although you can build Angular applications with native JavaScript, we recommend you use TypeScript, because most of the examples, tutorials, documentation, code examples, and so on in the book will use TypeScript. In addition, the Angular framework itself is built with TypeScript.
TypeScript adds benefits needed for enterprise applications, such as annotations, static typing, and classical object-oriented features like interfaces and code encapsulation, while still providing the key features of JavaScript.
You’ll find the syntax of TypeScript to be much like that of JavaScript, because TypeScript is a superset of JavaScript. Figure 1.1 shows how TypeScript’s key features (outer circle) encompass the key features of the ES6 version of JavaScript (inner circle).
TypeScript includes most of the key ES6 features, so almost any valid ES6 or ES5 feature is also valid TypeScript. You could have a perfectly valid TypeScript file that uses only standard JavaScript syntax.
TypeScript compiles to JavaScript, and the Angular CLI will compile TypeScript automatically out of the box. If you’re familiar with JavaScript and have some knowledge of object-oriented programming (OOP), you should be able to pick up TypeScript quickly. If you’re new to OOP, that’s fine. It may just take a little more work for you to get up to speed.
The following listing creates a simple class called Cat
that demonstrates what a class looks like in TypeScript.
Listing 1.1 Simple TypeScript class example
export class Cat {
private _name: string = ''; ① ② ③
constructor(name? : string) { ① ④
this._name = name;
}
get name() : string { ①
return this._name;
}
set name(name : string) { ①
this._name = name;
}
toString() : string { ①
return `This cat's name is ${this._name}!`;
}
}
const cat = new Cat('Nicky');
This example demonstrates several features. The class
,
get
,
set
, and constructor
keywords, along with the template string found in the toString()
method, are all part of the ES6 version of JavaScript, so those keywords aren’t exclusive to TypeScript.
The features in the example that belong to TypeScript are the actual variable types, the private
access modifier, and the ?
operator, which allows for an optional parameter.
In the Cat
class, notice that the keyword string
is sprinkled throughout the example. This is how you specify the expected types in TypeScript. You can specify return types, variable types, and parameter types within your code. The ability to specify types—type annotations—is the key advantage of using TypeScript, hence the name.
We’ll be covering TypeScript throughout the book, but if you want to get your feet wet with TypeScript outside of that, visit http://www.typescriptlang.org. The website offers a Playground feature where you can write and test code.
Now that you’ve learned a bit about TypeScript, let’s look at the different types of tests that you’ll be writing in this book.
Unit tests and E2E tests are the two types of tests you’ll see in the wild for testing Angular applications. In this section, we’ll look a little more closely at those two types.
You write unit tests to test the functionality of basic parts or units of code. Each unit test should only test one responsibility of the source code. You can test functions, methods, objects, types, values, and more with unit tests. The advantages of using unit tests are that they tend to be fast, reliable, and repeatable, if you write them correctly and run them in the right environment.
Jasmine, a behavior-driven development framework for testing JavaScript code, is the framework you’ll use in this book for writing unit tests. The following listing shows the code for a basic unit test written using the Jasmine framework.
Listing 1.2 Example of a simple unit test
describe('super basic test', () => {
it('true is true', () => {
expect(true).toEqual(true); ①
});
});
All you’re doing in listing 1.2 is checking to see that the Boolean value true
is equal to the Boolean value true
. This test serves as a sanity check and nothing more. You can use a sanity check to see if all the parts of your testing environment are set up correctly, and you use it when you’re only attempting to get a basic test to pass. You wouldn’t want to add a test this simple to a production application.
The next listing shows a slightly more sophisticated unit test that tests getters and setters for the name
instance found in the Cat
class you created in listing 1.1.
Listing 1.3 Better example of a unit test
import { Cat } from './cat';
describe('Test Cat getters and setters.', () => {
it('The cat name should be Gracie', () => {
const cat = new Cat();
cat.name = 'Gracie';
expect(cat.name).toEqual('Gracie'); ①
});
});
Although unit tests tend to be reliable, they aren’t the best type of test for reproducing real user interactions.
You use E2E tests to test the functionality of an application by simulating the behavior of an end user. For example, you might have an E2E test check if a modal correctly appears after a form is submitted or a page renders certain elements on page load, such as buttons or text.
E2E tests do a good job with testing applications from an end user’s standpoint, but they can run slowly, and that slowness can be the source of false positives that fail tests because of timeout issues. E2E tests’ timing issues make it preferable to write unit tests instead of E2E tests whenever possible.
For writing the E2E tests in this book, you’ll use the Protractor E2E test framework developed by the Angular team.
The following listing shows a sample E2E test written using Protractor that checks the sample project’s website and makes sure the title of the page is equal to Contacts App Starter
.
Listing 1.4 Sample E2E test using Protractor
import { browser } from 'protractor';
describe('Contacts App title test', () => { ①
it('Title should be correct', () => { ②
const appUrl = 'https://testing-angular-applications.github.io';
const expectedTitle = 'Contacts App Starter';
browser.get(appUrl);
browser.getTitle().then((actualTitle) => {
expect(actualTitle).toEqual(expectedTitle); ③
});
});
});
We’ll explore unit tests and E2E tests in much greater detail throughout the book. If you understand these two types of tests, you’ll understand much of Angular testing.
Unit tests and E2E tests have different advantages. We’ve discussed these advantages a little already, but in table 1.2, we summarize the pros and cons of the different types of tests.
Feature | Unit tests | E2E tests |
Speed | Tend to be faster than E2E tests. | Tend to be slower than unit tests. |
Reliability | Tend to be more reliable than E2E tests. | Tests can be flaky and fail because they may time out while executing. |
Helping enforce code quality | Writing tests can help identify needlessly complex code that may be difficult to test. | Tests from the browser won’t help write better code because you’re testing the app as a whole from the outside. |
Cost-effectiveness | More cost-effective because of developer time to write tests, execution of tests, and reliability. | Less cost-effective because it takes longer to write tests, execution of tests is slow, and tests can be flaky. |
Mimicking user interactions | Tests can mimic user interactions but can be hard to use to check complex interactions. | Mimicking user interactions is E2E tests’ forte and what they’re made for. |
Let’s discuss each of these features one by one:
Writing E2E tests won’t help you write better quality code per se. Because E2E tests test from the browser standpoint, they don’t directly test your code.
Both types of tests are important to have to thoroughly test your applications. You can test a lot of the functionality that a user would perform by writing unit tests, but you should test key functionality with E2E tests.
Generally, you want to have more unit tests than E2E tests in your project. A software developer named Mike Cohn created a testing pyramid to show how the different types of tests should be broken down. In figure 1.2, you can see approximate percentages for the different tests you should include in your project.
You may have noticed that the pyramid includes integration tests. Integration tests are used to test that a whole system works correctly. For our purposes, we’ll roll the integration tests into E2E tests, because E2E tests test the entire UI system.
That gives you a basic background on Angular testing. In the next chapter, you’ll get the chance to dive in to writing your first real tests.
18.224.39.74