TestBed.configureTestingModule({ // Provide both the service-to-test and its (spy) dependency providers: [ MasterService, //注入服务,mock提供依赖服务的支持,完成MasterService实例创建 { provide: ValueService, useValue: spy } ] }); // Inject both the service-to-test and its (spy) dependency masterService = TestBed.get(MasterService); valueServiceSpy = TestBed.get(ValueService); });
1 2 3 4 5 6 7
it('#getValue should return stubbed value from a spy', () => { const stubValue = 'stub value'; # mock 返回值 valueServiceSpy.getValue.and.returnValue(stubValue); expect(masterService.getValue()) .toBe(stubValue, 'service returned stub value'); //利用mock依赖返回的值,进行期望判断业务逻辑 });
测试组件-无依赖
代码如下:
1 2 3 4 5 6 7 8 9 10 11
@Component({ selector: 'lightswitch-comp', template: ` <button (click)="clicked()">Click me!</button> <span>{{message}}</span>` }) export class LightswitchComponent { isOn = false; clicked() { this.isOn = !this.isOn; } get message() { return `The light is ${this.isOn ? 'On' : 'Off'}`; } }
//直接new it('#clicked() should set #message to "is on"', () => { const comp = new LightswitchComponent(); expect(comp.message).toMatch(/is off/i, 'off at first'); comp.clicked(); expect(comp.message).toMatch(/is on/i, 'on after clicked'); }); //or 获取组件实例,交给框架创建new let comp:LightswitchComponent; beforeEach(() => { TestBed.configureTestingModule({ // provide the component-under-test and dependent service providers: [ LightswitchComponent, ] }); // inject both the component and the dependent service. comp = TestBed.get(LightswitchComponent); }); it('#clicked() should set #message to "is on"', () => { expect(comp.message).toMatch(/is off/i, 'off at first'); comp.clicked(); expect(comp.message).toMatch(/is on/i, 'on after clicked'); });
测试组件-有input、output
1 2 3 4 5
export class DashboardHeroComponent { @Input() hero: Hero; @Output() selected = new EventEmitter<Hero>(); click() { this.selected.emit(this.hero); } }
测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
let comp:DashboardHeroComponent; beforeEach(() => { TestBed.configureTestingModule({ // provide the component-under-test and dependent service providers: [ DashboardHeroComponent, ] }); // inject both the component and the dependent service. comp = TestBed.get(DashboardHeroComponent); }); it('raises the selected event when clicked', () => { const hero: Hero = { id: 42, name: 'Test' }; comp.hero = hero;
# spect.ts class MockUserService { isLoggedIn = true; user = { name: 'Test User'}; };
beforeEach(() => { TestBed.configureTestingModule({ // provide the component-under-test and dependent service providers: [ WelcomeComponent, { provide: UserService, useClass: MockUserService } // {provide: UserService, useVale: userServiceSpy} # 两者都可以,不同方式而已 ] }); // inject both the component and the dependent service. comp = TestBed.get(WelcomeComponent); //容易记住,也不太冗长。但是,只有当Angular在测试的根注入器中将带有服务实例的组件注入组件时,它才起作用。 userService = TestBed.get(UserService); //userService = fixture.debugElement.injector.get(UserService); });
1 2 3 4 5 6 7 8 9 10 11 12 13
it('should not have welcome message after construction', () => { expect(comp.welcome).toBeUndefined(); }); it('should welcome logged in user after Angular calls ngOnInit', () => { comp.ngOnInit(); expect(comp.welcome).toContain(userService.user.name); }); it('should ask user to log in if not logged in after ngOnInit', () => { userService.isLoggedIn = false; comp.ngOnInit(); expect(comp.welcome).not.toContain(userService.user.name); expect(comp.welcome).toContain('log in'); });
it('should display error when TwainService fails', fakeAsync(() => { //fakeAsync不适用与ajax getQuoteSpy.and.returnValue( throwError('TwainService test failure'));
fixture.detectChanges(); // onInit() tick(); // flush the component's setTimeout() fixture.detectChanges(); // update errorMessage within setTimeout()
expect(errorMessage()).toMatch(/test failure/, 'should display error'); expect(quoteEl.textContent).toBe('...', 'should show placeholder'); }));
异步代码测试
使用fakeAsync
1 2 3 4 5 6 7
it('should get Date diff correctly in fakeAsync', fakeAsync(() => { const start = Date.now(); tick(100); const end = Date.now(); expect(end - start).toBe(100); }));
fakeAsync支持以下异步任务:
setTimeout
setInterval
requestAnimationFrame
webkitRequestAnimationFrame
mozRequestAnimationFrame
rxjs - delay、interval等
ajax请求测试
1 2 3 4 5 6 7 8 9 10
it('should show quote after getQuote (async)', async(() => { fixture.detectChanges(); // ngOnInit() expect(quoteEl.textContent).toBe('...', 'should show placeholder');
fixture.whenStable().then(() => { // wait for async getQuote fixture.detectChanges(); // update view with quote expect(quoteEl.textContent).toBe(testQuote); expect(errorMessage()).toBeNull('should not show error'); }); }));
jasmine done
1 2 3 4 5 6 7 8 9 10 11
it('should show quote after getQuote (spy done)', (done: DoneFn) => { fixture.detectChanges();
// the spy's most recent call returns the observable with the test quote getQuoteSpy.calls.mostRecent().returnValue.subscribe(() => { fixture.detectChanges(); // update view with quote expect(quoteEl.textContent).toBe(testQuote); expect(errorMessage()).toBeNull('should not show error'); done(); }); });
组件嵌套测试
服务依赖错误
1
TypeError: ctor is not a constructor
问题原因:provide中错误的配置
1 2
//错误的 providers: [{provide: OrderService, useClass: new OrderServiceMock()}]
1 2
//正确的 providers: [{provide: OrderService, useValue: new OrderServiceMock()}]
HTTP service测试
类似service测试,使用Spy
使用HttpTestingController
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14
let service: BlogPostsService; let backend: HttpTestingController; beforeEach(() => { TestBed.configureTestingModule({ providers: [BlogPostsService], imports: [ HttpClientTestingModule ] }); }); beforeEach(() => { service = TestBed.get(BlogPostsService); backend = TestBed.get(HttpTestingController); });
it('should not expect one when not subscribed', () => { service.getAll()// .subscribe(); backend.expectNone(`https://rails-rest.herokuapp.com/posts`); backend.verify(); });