按照Ari Lerner ng-book2上的代码示例,并使用Angular 2 beta 7,我试图模拟并监视对服务的调用失败。
这是使用该服务的主要组件:
用户list.component.ts
import {Component, OnInit} from 'angular2/core';
import {UserService} from './user.service';
import {IUser} from './user.model';
@Component({
selector: 'user-list',
providers: [UserService],
template: `
<div *ngFor="#user of users" class="user">
<span class="username">Username: {{ user.username }}</span><br>
<span class="email">Email: {{ user.email }}</span>
</div>
`
})
export class UserListComponent implements OnInit {
public users: IUser[];
private userService: UserService;
constructor(userService: UserService) {
this.userService = userService;
}
ngOnInit(): void {
this.userService.getAllUsers().subscribe(
(users: IUser[]) => {
this.users = users;
},
(error: any) => {
console.log(error);
}
);
}
}
这就是服务本身。
user.service.ts
import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/Rx';
import {IUser} from './user.model';
@Injectable()
export class UserService {
private http: Http;
private baseUrl: string = 'http://jsonplaceholder.typicode.com/users';
constructor(http: Http) {
this.http = http;
}
public getAllUsers(): Observable<IUser[]> {
return this.http.get(this.baseUrl)
.map(res => res.json());
}
}
为了测试UserListComponent
,我尝试使用以下代码模拟UserService
并监听其方法调用getAllUser
:
用户list.component.spec.ts
import {
describe,
expect,
it,
injectAsync,
TestComponentBuilder,
ComponentFixture,
setBaseTestProviders,
} from 'angular2/testing';
import {SpyObject} from 'angular2/testing_internal';
import {
TEST_BROWSER_PLATFORM_PROVIDERS,
TEST_BROWSER_APPLICATION_PROVIDERS
} from 'angular2/platform/testing/browser';
import {provide} from 'angular2/core';
import {UserListComponent} from './user-list.component';
import {UserService} from './user.service';
class SpyUserService extends SpyObject {
public getAllUsers: Function;
public fakeResponse: any = null;
constructor() {
super(UserService);
this.getAllUsers = this.spy('getAllUsers').andReturn(this);
}
public subscribe(callback) {
callback(this.fakeResponse);
}
public setResponse(data: any): void {
this.fakeResponse = data;
}
}
describe('When rendering the UserListComponent and mocking the UserService', () => {
setBaseTestProviders(TEST_BROWSER_PLATFORM_PROVIDERS, TEST_BROWSER_APPLICATION_PROVIDERS);
it('should show one mocked user', injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
let spyUserService = new SpyUserService();
spyUserService.setResponse([{
username: 'ryan',
email: 'ryan@gmail.com'
}]);
return tcb
.overrideProviders(UserListComponent, [provide(UserService, {useValue: spyUserService})])
.createAsync(UserListComponent)
.then((fixture: ComponentFixture) => {
fixture.detectChanges();
expect(spyUserService.getAllUsers).toHaveBeenCalled();
});
}));
});
当使用业力来运行测试时,我收到以下控制台错误:
Chrome 48.0.2564 (Mac OS X 10.11.3) ERROR
Uncaught TypeError: Cannot read property 'isSlow' of null
at /Users/david/apps/sandbox/angular2-testing-cookbook/src/tests.entry.ts:19430
有人知道为什么会抛出这个错误,或者模拟和间谍服务单元测试Angular 2组件的正确方法是什么?
答案 0 :(得分:24)
我采用了稍微不同的方法,并使用注入自己通过DI来获取服务实例。所以你的测试看起来像:
import {
describe,
expect,
it,
tick,
inject,
fakeAsync,
TestComponentBuilder,
ComponentFixture,
addProviders
} from 'angular2/testing';
import { Component, provide } from '@angular/core';
import {UserListComponent} from './user-list.component';
import {UserService} from './user.service';
import {MockUserService} from './user.service.mock';
describe('When loading the UserListComponent', () => {
beforeEach(() => addProviders([
{provide: UserService, useClass: MockUserService}
]));
it('should call the getAllUsers method from the UserService',
inject([TestComponentBuilder, UserService], fakeAsync((tcb: TestComponentBuilder, mockUserService: UserService) => {
spyOn(mockUserService, 'getAllUsers');
tcb
.createAsync(UserListComponent)
.then((fixture: ComponentFixture) => {
tick();
fixture.detectChanges();
expect(mockUserService.getAllUsers).toHaveBeenCalled();
});
}))
);
it('should show one mocked user',
inject([TestComponentBuilder, UserService], fakeAsync((tcb: TestComponentBuilder, mockUserService: UserService) => {
mockUserService.setResponse([{
username: 'ryan',
email: 'ryan@gmail.com'
}]);
tcb
.createAsync(UserListComponent)
.then((fixture: ComponentFixture) => {
tick();
fixture.detectChanges();
let compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('div:nth-child(1) .username')).toHaveText('Username: ryan');
expect(compiled.querySelector('div:nth-child(1) .email')).toHaveText('Email: ryan@gmail.com');
});
}))
);
});
编辑Angular 4
最新的docs有一种更简单的方法来使用组件的注入器来获取服务:
fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
const mockService = fixture.debugElement.injector.get(MyService);
答案 1 :(得分:5)
我找到了解决方案
<强> test.entry.ts 强>
import {setBaseTestProviders} from 'angular2/testing';
import {
TEST_BROWSER_PLATFORM_PROVIDERS,
TEST_BROWSER_APPLICATION_PROVIDERS
} from 'angular2/platform/testing/browser';
setBaseTestProviders(TEST_BROWSER_PLATFORM_PROVIDERS, TEST_BROWSER_APPLICATION_PROVIDERS);
import './user-list.component.spec';
用户-list.component.spec.ts 强>
import {
describe,
expect,
it,
tick,
inject,
fakeAsync,
TestComponentBuilder,
ComponentFixture,
beforeEachProviders
} from 'angular2/testing';
import {UserListComponent} from './user-list.component';
import {MockUserService} from './user.service.mock';
describe('When loading the UserListComponent', () => {
let mockUserService: MockUserService;
beforeEachProviders(() => {
mockUserService = new MockUserService();
return [mockUserService.getProvider()];
});
it('should call the getAllUsers method from the UserService',
inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
spyOn(mockUserService, 'getAllUsers');
tcb
.createAsync(UserListComponent)
.then((fixture: ComponentFixture) => {
tick();
fixture.detectChanges();
expect(mockUserService.getAllUsers).toHaveBeenCalled();
});
}))
);
it('should show one mocked user',
inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
mockUserService.setResponse([{
username: 'ryan',
email: 'ryan@gmail.com'
}]);
tcb
.createAsync(UserListComponent)
.then((fixture: ComponentFixture) => {
tick();
fixture.detectChanges();
let compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('div:nth-child(1) .username')).toHaveText('Username: ryan');
expect(compiled.querySelector('div:nth-child(1) .email')).toHaveText('Email: ryan@gmail.com');
});
}))
);
});
<强> user.service.mock.ts 强>
import {provide, Provider} from 'angular2/core';
import {UserService} from './user.service';
import * as Rx from 'rxjs/Rx';
export class MockUserService {
public fakeResponse: any = null;
public getAllUsers(): Rx.Observable<any> {
let subject = new Rx.ReplaySubject()
subject.next(this.fakeResponse);
return subject;
}
public setResponse(response: any): void {
this.fakeResponse = response;
}
public getProvider(): Provider {
return provide(UserService, {useValue: this});
}
}
答案 2 :(得分:5)
其他解决方案对我不起作用,所以我从injector
注入debugElement
服务。
import { TestBed,
async } from '@angular/core/testing';
@injectable()
class MyService {
public method () {}
}
let MyMockedService = {
method: () => {}
}
@Component({
template: ''
})
class MyComponent {
constructor(private myService: MyService) {;}
public method () {
this.myService.method();
}
}
describe('Test', () => {
beforeEach(async(() => {
TestBed
.configureTestingModule({
imports: [
CommonModule
],
declarations: [
MyComponent
],
providers: [
{ provide: MyService, useValue: MyMockedService}
]
})
.compileComponents()
.then(() => {
fixture = TestBed.createComponent(MyComponent);
myComponent = fixture.componentInstance;
});
}));
it('should spy on service', () => {
let myMockedService = fixture.debugElement.injector.get(MyMockedService);
spyOn(myMockedService, 'method');
myComponent.method();
expect(myMockedService.method);
});
})
答案 3 :(得分:4)
对于所有以角度2进行测试的新手(与我在这里相同)
如果您安装了正确的jasmine类型,则createSpy或SpyOn方法仅“可用”。否则会引发打字稿错误。
检入您的typings.json文件,如果不存在,请运行此
typings install --save --global registry:dt / jasmine