Angular 7 + unit testing + code coverage

Manivel Arjunan
7 min readDec 9, 2018

--

  1. What is unit testing?
  2. Why unit testing is important?
  3. Angular 7 application configuration for unit testing, debug and code coverage.
  4. Basic Component unit testing
  5. What is a destructuring assignment and why not to use beforeEach.
  6. Synchronous and Async service(Observables implementation)unit testing using TestBed, Async, fakeAsync and tick utilities.
  7. What is isolation testing?
  8. How to test HTTP service?
  9. What are fdescribe and fit (Skip the test(s) execution).
  10. Why put spec file next to the file it tests?
Jasmine & karma
  1. What is unit testing?

Unit testing is a software development process in which the smallest testable parts of an application, called units, are individually and independently scrutinized for proper operation. Unit testing can be done manually but it is often automated.

2. Why unit testing is important?

  1. Unit testing Analyze the code behavior (expected and unexpected changes).
  2. It behaves as a safeguard against breaking changes. One of the best ways to keep your project bug free is through a test suite.
  3. Makes developers feel guilty free on their code changes.
  4. It also reveals the design mistakes.

3. Angular 7 application configuration for unit testing debug and code coverage.

3.1 configuration

The Angular CLI downloads and install everything you need to test an Angular application with the Jasmine test framework.

When you create a component, directive, pipe etc through Angular CLI, spec file will be created automatically with the default test suite.

Once you created a project through Angular CLI/ cloning from a remote repository, go to project root directory and run the below command to run the test suite.

ng test

and you will see the below logs in the console.

ng test logs

Also, ng test opens up chrome window to view the success and failed test cases.

3.2 debug

Debug specs in the browser in the same way that you debug an application.

  1. Reveal the karma browser window (hidden earlier).
  2. Click the DEBUG button; it opens a new browser tab and re-runs the tests.
  3. Open the browser’s “Developer Tools” (Ctrl+Shift+I on windows; command + option + I in OSX).
  4. Pick the “sources” section.
  5. Open the app.component.spec.ts test file (Control/Command-P, then start typing the name of the file).
  6. Set a breakpoint in the test.
  7. Refresh the browser, and it stops at the breakpoint.

Below diagram depicts how to debug.

Debug the spec file

3.3 code coverage

If you want to create code-coverage reports every time you run the test suite, you can set the following option in the CLI configuration file, angular.json:

"test":{ 
"options":{
"codeCoverage": true
}
}

(or) run the below command in the project root directory.

ng test --code-coverage

Whenever test suite executed successfully, you will see a “coverage” folder in project root directory.

coverage folder

Open the index.html file in your favorite browser to view the test case coverage for all the files in the project.

istanbul code coverage report

4. Component unit testing with angular testing utilities.

What is TestBed?

The TestBed is the most important of the Angular testing utilities. It creates a dynamically-constructed Angular test module that emulates an Angular @NgModule.TestBed.configureTestingModule() method takes a metadata object that can have most of the properties of an @NgModule.

The TestBed API consists of static class methods that either update or reference a global instance of the TestBed.

Internally, all static methods cover methods of the current runtime TestBed instance, which is also returned by the getTestBed() function.

Call TestBed methods within a beforeEach() to ensure a fresh start before each individual test.

beforeEach(async(() => {TestBed.configureTestingModule({declarations: [AppComponent, UserComponent, UserAsyncComponent]}).compileComponents();}));

What is a component fixture?

The TestBed.createComponent<T> creates an instance of the component T and returns a strongly typed for that component.

The ComponentFixture properties and methods provide access to the component, its DOM representation, and aspects of its Angular environment.

The fixture methods cause Angular to perform certain tasks on the component tree. Call these methods to trigger Angular behavior in response to simulated user action.

What is DebugElement?

The DebugElement provides crucial insights into the component's DOM representation.

From the test root component’s DebugElement returned by fixture.debugElement, you can walk (and query) the fixture's entire element and component subtrees.

Basic component testing

A component, unlike all other parts of an Angular application, combines an HTML template and a TypeScript class. The component truly is the template and the class working together and to adequately test a component, you should test that they work together as intended.

Such tests require creating the component’s host element in the browser DOM, as Angular does, and investigating the component class’s interaction with the DOM as described by its template.

Here is the example of Basis component testing using Angular TestBed utility.

app.component.ts
app.component.html
app.component.spec.ts

5. What is a destructuring assignment(setup method) and why is it better than using beforeEach()

Note that, in above spec file I used destructing assignment instead of BeforeEach. Most test suites call beforeEach() to set the preconditions for each it() test and rely on the TestBed to create classes and inject services.

There’s another school of testing that never calls beforeEach() and prefers to create classes explicitly rather than use the TestBed.The setup() function returns an object literal with the variables, such as app, that a test might reference. You don’t define semi-global variables (e.g., let app, fixture ) in the body of the describe(). Then each test invokes setup() in its first line, before continuing with steps that manipulate the test subject and assert expectations.

Angular team recommands to use setup function instead of traditional beforeEach()

Here is the spec file using traditional beforeEach().

app.component.spec.ts

Synchronous service unit test

Testing synchronous service which is injected in a component. Injecting the real service rarely works well as most dependent services are difficult to create and control.

user.component.ts
user.component.html
user.service.ts

Injecting service in a spec file.

const userService = fixture.debugElement.injector.get(UserService);

Prefer spies as they are usually the easiest way to mock services.

Spy the service and return the custom values.

const mockUser = { name: 'Mannie' };spyOn(userService, 'getUser').and.returnValue(mockUser);

Update the properties, run the change detection. You must tell the TestBed to perform data binding by calling fixture.detectChanges()

fixture.detectChanges();
user.component.spec.ts

Async service(Observables implementation) unit testing

user-async.component.ts
user-async.component.html

Creating Async service using Observable, observable returns username after 2 milliseconds

user-async.service.ts

tick, fakeAsync testing utilities are used to test Async service.

fakeAsync — wrap “it” with fakeAsync to create Async environment for test.it allows running Async task and it simulates like Async call running in the browser.

tick — Wait for Async calls finishes and successfully to access the data. Simulates the passage of time and the completion of pending asynchronous activities by flushing both timer and micro-task queues within the fakeAsync test zone.

user-async.component.spec.ts

7. Isolation testing

Pipes are easy to test without the Angular testing utilities. These are tests of the pipe in isolation. Files which are doesn’t require angular dependency can be tested without angular testing utilities.

reverse-value.pipe.ts

Creating a test for pipe and it is an isolation testing since it doesn’t depend on Angular and doesn’t use testing utilities.

reverse-pipe.pipe.spec.ts

8. Testing HTTP service

Data services that make HTTP calls to remote servers typically inject and delegate to the Angular HTTPClient service for XHR calls.

9. fdescribe and fit ( skip the test or run only specific tests)

Add fdescribe to run the test for a spec file or a test suite. It skips all other files or runs test for the specified file.

fdescribe('UserComponent', () => {beforeEach(async(() => {TestBed.configureTestingModule({declarations: [UserComponent]});

Add fit to run the test for “it” level. It skips all other tests and runs only one test.

fit('should create app component', () => {const { component } = setup();expect(component).toBeTruthy();});

10. Why put spec file next to the file it tests?

It’s a good idea to put unit test spec files in the same folder as the application source code files that they test:

  • Such tests are easy to find.
  • You see at a glance if a part of your application lacks tests.
  • Nearby tests can reveal how a part works in context.
  • When you move the source (inevitable), you remember to move the test.
  • When you rename the source file (inevitable), you remember to rename the test file.

Below is the link for Github projects. It contains all the configuration and code explained above.

Below is the link for Angular official documentation for unit testing.

--

--

Manivel Arjunan

Creative Senior front-end Developer. Love solving problems and keep updating/learning on new technologies.