在我的 Angular 2 应用程序中,我尝试对以下组件进行单元测试:
export class LoginComponent implements OnInit {
invalidCredentials = false;
unreachableBackend = false;
constructor(private authService: AuthService) {}
ngOnInit() {
this.invalidCredentials = false;
}
onSubmit(user: any) {
this.authService.authenticateUser(<User>user).subscribe((result) => {
if (!result) {
this.invalidCredentials = true;
}
}, (error) => {
if (error instanceof InvalidCredentialsError) {
this.invalidCredentials = true;
} else {
this.unreachableBackend = true;
}
});
}
}
我已经成功测试了happy path。现在我想检查当 authService.authenticateUser() 抛出错误时,invalidCredentials 和 unreachableBackend 是否设置正确。这是我正在尝试的:
describe('Authentication triggering an error', () => {
class FakeAuthService {
authenticateUser(user: User) {
throw new InvalidCredentialsError();
}
}
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
let authService: AuthService;
let spy: Spy;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [LoginComponent],
providers: [
{provide: AuthService, useClass: FakeAuthService},
{provide: TranslateService, useClass: FakeTranslateService}
],
imports: [ FormsModule, TranslateModule, AlertModule ]
});
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
authService = fixture.debugElement.injector.get(AuthService);
spy = spyOn(authService, 'authenticateUser').and.callThrough();
});
it('should not log in successfully if authentication fails', () => {
const user = {username: 'username', password: 'password'};
component.onSubmit(user);
expect(authService.authenticateUser).toHaveBeenCalledWith(user);
expect(spy.calls.count()).toEqual(1, 'authenticateUser should have been called once');
expect(component.invalidCredentials).toBe(true, 'credentials should be invalid because of the exception');
expect(component.unreachableBackend).toBe(false, 'backend should be reachable at first');
});
});
但是当我运行这个测试时,我遇到了以下错误:
PhantomJS 2.1.1 (Mac OS X 0.0.0) Component: Login Authentication triggering an error should not log in successfully if authentication fails FAILED [object Object] thrown in src/test.ts (line 49782) authenticateUser@webpack:///Users/sarbogast/dev/myproject/frontend/src/app/login/login.component.spec.ts:112:44 <- src/test.ts:49782:67 onSubmit@webpack:///Users/sarbogast/dev/myproject/frontend/src/app/login/login.component.ts:9:4896 <- src/test.ts:85408:5955 webpack:///Users/sarbogast/dev/myproject/frontend/src/app/login/login.component.spec.ts:139:25 <- src/test.ts:49806:31 invoke@webpack:///Users/sarbogast/dev/myproject/frontend/~/zone.js/dist/zone.js:203:0 <- src/test.ts:84251:33 onInvoke@webpack:///Users/sarbogast/dev/myproject/frontend/~/zone.js/dist/proxy.js:72:0 <- src/test.ts:59204:45 invoke@webpack:///Users/sarbogast/dev/myproject/frontend/~/zone.js/dist/zone.js:202:0 <- src/test.ts:84250:42 run@webpack:///Users/sarbogast/dev/myproject/frontend/~/zone.js/dist/zone.js:96:0 <- src/test.ts:84144:49 webpack:///Users/sarbogast/dev/myproject/frontend/~/zone.js/dist/jasmine-patch.js:91:27 <- src/test.ts:58940:53 execute@webpack:///Users/sarbogast/dev/myproject/frontend/~/zone.js/dist/jasmine-patch.js:119:0 <- src/test.ts:58968:46 execute@webpack:///Users/sarbogast/dev/myproject/frontend/~/zone.js/dist/jasmine-patch.js:119:0 <- src/test.ts:58968:46 invokeTask@webpack:///Users/sarbogast/dev/myproject/frontend/~/zone.js/dist/zone.js:236:0 <- src/test.ts:84284:42 runTask@webpack:///Users/sarbogast/dev/myproject/frontend/~/zone.js/dist/zone.js:136:0 <- src/test.ts:84184:57 drainMicroTaskQueue@webpack:///Users/sarbogast/dev/myproject/frontend/~/zone.js/dist/zone.js:368:0 <- src/test.ts:84416:42 PhantomJS 2.1.1 (Mac OS X 0.0.0): Executed 33 of 38 (1 FAILED) (skipped 5) (0.704 secs / 0.842 secs)
所以显然有些东西我没有得到。我应该提一下,我对 JS 单元测试完全陌生,对响应式(Reactive)编程也有些陌生。
最佳答案
你不能那样做,因为错误只会被抛出并冒泡到测试中。您需要做的是允许用户订阅回调,然后您可以调用错误回调。例如
class FakeAuthService {
authenticateUser(user: User) {
// return this service so user can call subscribe
return this;
}
subscribe(onNext, onError) {
if (onError) {
onError(new InvalidCredentialsError());
}
}
}
另一种选择是使用 Observable.throw(new InvalidCredentialsError())
authenticateUser(user: User) {
return Observable.throw(new InvalidCredentialsError());
}
这将导致调用任何订阅者的 onError 回调。我个人喜欢使用模拟返回本身。我可以使模拟更具可配置性,使测试更容易。有关我的意思的示例,请参见下面的链接。
另请参阅:
关于unit-testing - 当组件调用的服务抛出 Angular 2 异常时,测试组件是否正常运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40218828/