Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing in angular2, dependency injection

Starting out with angular 2 after spending time with angular 1. Not having unit tested this much as it's more of a side project thing, I'm trying at least start out OK... I started with the example from AngularClass if that makes a difference.

Struggling in app.component.ts already, which contains my navigation bits. Relevant bits of the template here:

<nav class="navbar navbar-light bg-faded">
  <div class="container">
    <div class="nav navbar-nav">
      <a class="navbar-brand" [routerLink]=" ['./'] ">Navbar</a>
      <loading class="nav-item nav-link pull-xs-right" [visible]="user === null"></loading>
  </div>
</div>
</nav>

<div class="container">
  <main>
    <router-outlet></router-outlet>
</main>
</div>

<footer>
  <hr>
  <div class="container">

</div>
</footer> 

Component itself does not contain much:

import { Component, ViewEncapsulation } from '@angular/core';
import { AuthService } from './_services';
import { User } from './_models';
import { Loading } from './_components';

@Component({
    selector: 'app',
    encapsulation: ViewEncapsulation.None,
    template: require('./app.component.html'),
    styles: [
        require('./app.style.css')
    ]
})
export class App {
    user: User;

    constructor(private auth: AuthService) {

    }

    ngOnInit() {
        this.auth.getUser().subscribe(user =>  this.user = user);
    }
}

All modules, components and routes are bootstrapped through the App module. Can post if required.

The test I'm having to write for it has me hooking up basically everything from the router (so it seems). First, [routerLink] is not a native attribute of 'a'. Ok, I fix it. Then:

Error in ./App class App - inline template:3:6 caused by: No provider for Router!

So, I hook up router, only to find:

Error in ./App class App - inline template:3:6 caused by: No provider for ActivatedRoute!

Which I added, to find out that:

Error in ./App class App - inline template:3:6 caused by: No provider for LocationStrategy!

By now, the test looks like:

import { inject, TestBed, async } from '@angular/core/testing';
import { AuthService } from './_services';
import { Router, RouterModule, ActivatedRoute } from '@angular/router';
import { AppModule } from './app.module';

// Load the implementations that should be tested
import { App } from './app.component';
import { Loading } from './_components';

describe('App', () => {
    // provide our implementations or mocks to the dependency injector
    beforeEach(() => TestBed.configureTestingModule({
        declarations: [App, Loading],
        imports: [RouterModule],
        providers: [
            {
                provide: Router,
                useClass: class {
                    navigate = jasmine.createSpy("navigate");
                }
            }, {
                provide: AuthService,
                useClass: class {
                    getAccount = jasmine.createSpy("getAccount");
                    isLoggedIn = jasmine.createSpy("isLoggedIn");
                }
            }, {
                provide: ActivatedRoute,
                useClass: class { }
            }
        ]
    }));

    it('should exist', async(() => {

        TestBed.compileComponents().then(() => {
            const fixture = TestBed.createComponent(App);

            // Access the dependency injected component instance
            const controller = fixture.componentInstance;

            expect(!!controller).toBe(true);
        });
    }));
});

I'm already mocking the inputs, this seems wrong to me. Am I missing something? Is there a smarter way of loading the whole app on a test, instead of bolting in every single dependency, all the time?

like image 702
Jorg Avatar asked Sep 29 '16 12:09

Jorg


People also ask

What is dependency injection in unit testing?

Dependency injection is a programming technique that makes a class independent of its dependencies. It achieves that by decoupling the usage of an object from its creation. This helps you to follow SOLID's dependency inversion and single responsibility principles, in order to write a good programme.

Does Angular have unit testing?

Angular Unit testing is the process of testing small and isolated pieces of code in your Angular application. This provides an added advantage to the users in the sense that they can add any new features without breaking any other part of their application.

What is SpyOn in Angular unit testing?

SpyOn is a Jasmine feature that allows dynamically intercepting the calls to a function and change its result.

Which of the following can be used to run unit tests in Angular?

Jasmine is the default test framework used with Angular.


1 Answers

For testing, you should use the RouterTestingModule instead of the RouterModule. If you want to add routes you can use withRoutes

imports: [
  RouterTestingModule.withRoutes(Routes) // same any normal route config
]

See Also

  • Angular 2 unit testing components with routerLink
  • Second half of this post for an idea of mock the ActivatedRoute. Sometimes you don't want the whole routing facility when unit testing. You can just mock the route.
like image 68
Paul Samsotha Avatar answered Oct 01 '22 03:10

Paul Samsotha