Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 unit test service stub value does not change?

I am working through Angular 2 testing examples here https://angular.io/docs/ts/latest/guide/testing.html

Welcome component

import { Component, OnInit } from '@angular/core';
import { UserService }       from './user.service';

@Component({
  selector: 'app-welcome',
  template: '<h3 class="welcome" ><i>{{welcome}}</i></h3>'
})
export class WelcomeComponent implements OnInit {
  welcome = '-- not initialized yet --';
  constructor(private userService: UserService) {     
  }

  ngOnInit(): void {
    this.welcome = this.userService.isLoggedIn ?
      'Welcome, ' + this.userService.user.name :
      'Please log in.';
  }
}

Test spec

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { WelcomeComponent } from './welcome.component';
import { UserService } from './user.service';

describe('WelcomeComponent', () => {
  let component: WelcomeComponent;
  let fixture: ComponentFixture<WelcomeComponent>;
  let de:      DebugElement;
  let el:      HTMLElement;
  let userServiceStub: UserService;
  let userService:UserService;

  beforeEach(async(() => {
    // Declare stub UserService for test purposes
    let userServiceStub = {
      isLoggedIn: true,
      user: { name: 'Test User'}
    };

    TestBed.configureTestingModule({
      declarations: [ WelcomeComponent ],
       providers:  [ {provide: UserService, useValue: userServiceStub } ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(WelcomeComponent);
    component = fixture.componentInstance;
    //userService = TestBed.get(UserService);
    userService = fixture.debugElement.injector.get(UserService);

    //  get the "welcome" element by CSS selector (e.g., by class name)
    de = fixture.debugElement.query(By.css('.welcome'));
    el = de.nativeElement;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should welcome the user', () => {
    fixture.detectChanges();
    const content = el.textContent;
    expect(content).toContain('Welcome', '"Welcome ..."');
    expect(content).toContain('Test User', 'expected name');
  });

  it('should welcome "Bubba"', () => {
    userService.user.name = 'Bubba'; 
    fixture.detectChanges();
    expect(el.textContent).toContain('Bubba');
  });

  it('should request login if not logged in', () => {
    userService.isLoggedIn = false; 
    fixture.detectChanges();
    const content = el.textContent;
    expect(content).not.toContain('Welcome', 'not welcomed');
    expect(content).toMatch(/log in/i, '"log in"');
  });

});

The last 2 it() tests failed because the service stub values didn't change after

userService.user.name = 'Bubba';
fixture.detectChanges();

and

userService.isLoggedIn = false; 
fixture.detectChanges();

Any idea why Angular test module failed to detect change?

Uodate

I found it. fixture.detectChanges() triggers ngOnInit(). fixture.detectChanges() in beforeEach() is the culprit. Because it has initialized the component, the fixture.detectChanges() in it() won't initialized the component again. So by removing fixture.detectChanges() from beforeEach() the fixture.detectChanges() in it() will have the chance to trigger ngOnInit() and all tests pass through.

  beforeEach(() => {
    fixture = TestBed.createComponent(WelcomeComponent);
    component = fixture.componentInstance;
    //userService = TestBed.get(UserService);
    userService = fixture.debugElement.injector.get(UserService);

    //  get the "welcome" element by CSS selector (e.g., by class name)
    de = fixture.debugElement.query(By.css('.welcome'));
    el = de.nativeElement;
    // fixture.detectChanges(); // this is the culprit.
  });
like image 915
Shawn Avatar asked Feb 12 '17 05:02

Shawn


1 Answers

Try to initilize your service at your "it"

 it('should welcome "Bubba"' , inject([UserService], (userService: UserService) => {
     userService.user.name = 'Bubba'; 
     fixture.detectChanges();
     expect(el.textContent).toContain('Bubba');
  }));
like image 154
Yoav Schniederman Avatar answered Sep 28 '22 18:09

Yoav Schniederman