我有一个返回Observable
的服务函数,我正在我的一个组件中使用该服务。我使用发布here的技术为组件编写了单元测试。然而,奇怪的是我得到了"无法阅读财产'订阅'未定义"。
待测组件:
@Component({
moduleId: module.id,
templateUrl: 'signup.component.html'
})
export class SignupComponent implements OnInit {
signupForm: FormGroup;
submitted = false;
registered = false;
constructor(public router: Router, private fb: FormBuilder, public authService: AuthService) {
this.signupForm = this.fb.group({
'firstName': ['', Validators.required],
'lastName': ['', Validators.required],
'email': ['', Validators.compose([Validators.required])],
'password': ['', Validators.compose([Validators.required])],
'confirmPassword': ['', Validators.required]
});
}
ngOnInit() {
}
signup(event: any) {
event.preventDefault();
this.submitted = true;
if (!this.signupForm.valid) return;
this.authService.register(this.signupForm.value)
.subscribe(
(res: Response) => {
if (res.ok) {
this.registered = true;
}
},
(error: any) => {
this.registered = false;
console.log (error.status + ': ' + error.message);
});
}
}
AbstractMockObservableService
import { Subscription } from 'rxjs/Subscription';
export abstract class AbstractMockObservableService {
protected _subscription: Subscription;
protected _fakeContent: any;
protected _fakeError: any;
set error(err: any) {
this._fakeError = err;
}
set content(data: any) {
this._fakeContent = data;
}
get subscription(): Subscription {
return this._subscription;
}
subscribe(next: Function, error?: Function, complete?: Function): Subscription {
this._subscription = new Subscription();
spyOn(this._subscription, 'unsubscribe');
if (next && this._fakeContent && !this._fakeError) {
next(this._fakeContent);
}
if (error && this._fakeError) {
error(this._fakeError);
}
if (complete) {
complete();
}
return this._subscription;
}
}
最后是单元测试
import { ComponentFixture, TestBed, async, fakeAsync, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement, Component, NO_ERRORS_SCHEMA } from '@angular/core';
import { Location } from '@angular/common';
import { Router } from '@angular/router';
import { FormBuilder } from "@angular/forms";
import { SignupComponent } from './signup.component';
import { AuthService } from "../index";
import { AbstractMockObservableService } from './abstract-mock-observable-service'
let validUser = {
firstName: "Ahmad", lastName: "Qarshi", email: "qarshi@gmail.com"
password: "ahmad#123", confirmPassword: "ahmad#123"
}
class RouterStub {
navigate(url: string) { return url; }
}
class LocationStub {
path(url?: string) { return '/security/login'; }
}
class AuthServiceStub extends AbstractMockObservableService {
register(model: any) {
return this;
}
}
let authServiceStub: AuthServiceStub;
let comp: SignupComponent;
let fixture: ComponentFixture<SignupComponent>;
let de: DebugElement;
let el: HTMLElement;
function updateForm(firstName: string, lastName: string, email: string
password: string, confPassword: string) {
comp.signupForm.controls['firstName'].setValue(firstName);
comp.signupForm.controls['lastName'].setValue(lastName);
comp.signupForm.controls['email'].setValue(email);
comp.signupForm.controls['password'].setValue(password);
comp.signupForm.controls['confirmPassword'].setValue(confPassword);
}
describe('SignupComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [SignupComponent],
schemas: [NO_ERRORS_SCHEMA]
});
});
compileAndCreate();
tests();
});
function compileAndCreate() {
beforeEach(async(() => {
authServiceStub = new AuthServiceStub();
TestBed.configureTestingModule({
providers: [
{ provide: Router, useClass: RouterStub },
{ provide: Location, useClass: LocationStub },
{ provide: AuthService, useValue: authServiceStub },
FormBuilder
]
})
.compileComponents().then(() => {
fixture = TestBed.createComponent(SignupComponent);
comp = fixture.componentInstance;
});
}));
}
function tests() {
it('should call register user on submit', fakeAsync(() => {
comp.submitted = false;
updateForm(validUser.firstName, validUser.lastName, validUser.email, validUser.username, validUser.password,
validUser.confirmPassword, validUser.tin, validUser.userType);
spyOn(authServiceStub, 'register');
authServiceStub.content = { ok: true };
fixture.detectChanges();
tick();
fixture.detectChanges();
fixture.debugElement.query(By.css('input[type=submit]')).nativeElement.click();
expect(comp.submitted).toBeTruthy('request submitted');
expect(authServiceStub.register).toHaveBeenCalled();
expect(comp.registered).toBeTruthy('user registered');
}));
}
答案 0 :(得分:5)
当您监视服务方法并期望被调用时,您应该在spyOn上添加.and.callThrough()
。
您的代码应如下所示:spyOn(authServiceStub, 'register').and.callThrough();
然后,expect to haveBeenCalled()
。
看看:http://blog.danieleghidoli.it/2016/11/06/testing-angular-component-mock-services/
答案 1 :(得分:0)
您可以在app.module或单元测试
中导入interface Thing {
name: string;
age: number;
fav?: boolean; // You may need to mark this as optional depending on
// your requirement, otherwise TypeScript will
// generate missing prop error
}
//...
let newThing: Thing = Object.assign({}, oldThing, <Thing>{
name: "Bbb",
age: "abc", // will generate type error
fake: "fakey" // howerver, this will not generate unknown prop error
});