Search code examples
unit-testingangularjasminespy

Angular2 trigger button click in unit test and verify event handling


I'm trying to test if click event is properly handled. I'm trying to trigger click event on a button and check if proper call on router was executed. Don't know why stubRouteSpy did not registered call to navigate (last expect fails)

Template code:

<div class="col-lg-3 col-md-4 col-xs-6 thumb project" *ngFor="let project of projects">
    <div class = "thumbnail">
        <img class="img-responsive img-rounded" src="{{project.imgUrl}}" alt="">
        <div class = "caption">
         <h4 id='titleHeader'>{{project.title}}</h4>
         <div class="btn-group" role="group" style="all">
          <button class="btn btn-primary">Order</button>
          <button id='customizeButton' class="btn btn-info" (click)="onCustomize(project.id)">Customize</button>
        </div>
    </div>
</div>

Component code:

public errorMessage = '';
public projects = [];

constructor(private router: Router, private projectListService: ProjectListService) {
}

public ngOnInit() {
    this.getProjects();
}

public getProjects() {
    this.projectListService.getAllProjects()
        .subscribe(
            (projects) => this.projects = projects,
            (error) =>  this.errorMessage = <any> error);
}

public onCustomize(id: string) {
    console.log(id);
    let navigate = this.router.navigate(['design', id]);
}

Spec code:

describe('GalleryComponent (inline template)', () => {

    let comp: GalleryComponent;
    let fixture: ComponentFixture<GalleryComponent>;
    let projectListService: ProjectListService;
    let spy: jasmine.Spy;
    let de: DebugElement[];
    let stubRoute: Router;
    let stubRouteSpy: jasmine.Spy;

    beforeEach(() => {
        stubRoute = <Router> { navigate: ([]) => {}};
        TestBed.configureTestingModule({
            declarations: [GalleryComponent],
            providers: [
                ProjectListService,
                {provide: Router, useValue: stubRoute},
                {provide: Http, useValue: {}}
                ],
        });

        fixture = TestBed.createComponent(GalleryComponent);
        comp = fixture.componentInstance;

        // ProjectListService actually injected into the component
        projectListService = fixture.debugElement.injector.get(ProjectListService);

        // Setup spy on the `getAllProjects` method
        let fakeProjects = [new Project(1, 'title1', ''), new Project(2, 'title2', '')];
        spy = spyOn(projectListService, 'getAllProjects')
            .and.returnValue(Observable.of<Project[]>(fakeProjects));

        stubRouteSpy = spyOn(stubRoute, 'navigate');
    });

    it('should navigate to designer when customize button clicked', async(() => {
        fixture.detectChanges(); // init
        fixture.whenStable().then(() => { // wait for async getAllProjects
            fixture.detectChanges(); // update view with projects

            fixture.nativeElement.querySelectorAll('#customizeButton')[0].click();
            expect(fixture.nativeElement.querySelectorAll('#customizeButton').length).toBe(2); // this pass

            fixture.detectChanges();

            expect(stubRouteSpy.calls.any()).toBe(true, 'navigate called'); // this is false
        });
    }));
});

Solution

  • According the documentation here, you should get the provided RouterStub from the injector.

    I suggest you use the example in the documentation.

    the stub class:

    class RouterStub {
        navigateByUrl(url: string) { return url; }
    }
    

    provide:

    { provide: Router,      useClass: RouterStub }
    

    get injector:

    routerStub = fixture.debugElement.injector.get(RouterStub);