Angular 2单元测试服务存根值不变?

时间:2017-02-12 05:34:47

标签: angular typescript karma-jasmine

我正在研究Angular 2测试示例https://angular.io/docs/ts/latest/guide/testing.html

欢迎组件

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.';
  }
}

测试规范

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"');
  });

});

最后2个it()测试失败,因为服务存根值在

之后没有改变
userService.user.name = 'Bubba';
fixture.detectChanges();

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

为什么Angular测试模块无法检测到变化?

Uodate

我找到了。 fixture.detectChanges()触发ngOnInit()fixture.detectChanges()中的beforeEach()是罪魁祸首。由于它已初始化组件,fixture.detectChanges()中的it()将不会再次初始化组件。因此,通过从fixture.detectChanges()移除beforeEach()fixture.detectChanges()中的it()将有机会触发ngOnInit()并且所有测试都会通过。

  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.
  });

3 个答案:

答案 0 :(得分:2)

尝试在您的&#34; it&#34;

上启动您的服务
get()

答案 1 :(得分:0)

这是解决方案。

welcome.component.spec.ts

import { async, ComponentFixture, TestBed, inject } 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 from the root injector
    //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;
  });

  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"');
  });

});

答案 2 :(得分:0)

我遇到了同样的问题。感谢 redOctober13 指出问题:

  1. 如果您的 providers 数组中有 ComponentFixtureAutoDetect TestBed 配置和你使用 fixture.detectChanges(),它不会 在 it() 内工作。 ComponentFixtureAutoDetect 完美运行 输入等 html 更改很好,但不知何故在这里失败了。
  2. beforeEach() 中删除fixture.detectChanges() 并在更新it() 中的名称后使用它。