我试图测试一个基本上有电子邮件和密码字段的组件。填写完这两个字段后,用户可以单击该按钮并向服务器发送请求。当我通过浏览器测试这个组件时,它会执行我想要的行为。但是当我使用spec文件来测试组件时,subscribe
方法永远不会执行(obs:我嘲笑服务)。以下是组件的onSubmit
方法:
onSubmit(formAuthentication: NgForm) {
if (this.credential.email == undefined || this.credential.password == undefined) {
this.error = "Preencha o campo de email e de senha para continuar"
return;
} else {
this.tokenManagerService.generateNewToken(this.credential);
this.tokenManagerService.retrieveToken().subscribe(token => {
console.log(token);
const tokenRetrieved = JSON.parse(token);
if (tokenRetrieved.error) {
this.error = tokenRetrieved.error;
} else {
foo();
}
});
formAuthentication.resetForm();
}
}
这是spec文件和测试本身的配置:
describe('AuthenticationComponent', () => {
let component: AuthenticationComponent;
let fixture: ComponentFixture<AuthenticationComponent>;
let inputEmail: HTMLInputElement;
let inputPassword: HTMLInputElement;
let loginButton: HTMLInputElement;
let tokenManagerServiceMock: TokenManagerServiceMock;
let mockRouter = {
navigate: jasmine.createSpy('navigate')
}
beforeEach(() => {
tokenManagerServiceMock = new TokenManagerServiceMock();
TestBed.configureTestingModule({
declarations: [ AuthenticationComponent ],
imports: [ FormsModule, HttpModule,RouterTestingModule.withRoutes([]) ],
providers: [
{ provide: TokenManagerService, useValue: tokenManagerServiceMock }
]
});
TestBed.overrideComponent(AuthenticationComponent, {
set: {
providers: [
{ provide: TokenManagerService, useValue: tokenManagerServiceMock }
]
}
});
fixture = TestBed.createComponent(AuthenticationComponent)
fixture.componentInstance.ngOnInit();
tokenManagerServiceMock = TestBed.get(TokenManagerService);
fixture.detectChanges();
inputEmail = fixture.debugElement.nativeElement.querySelector('input[type=text]');
inputPassword = fixture.debugElement.nativeElement.querySelector('input[type=password]');
loginButton = fixture.nativeElement.querySelector('input[type=submit]');
});
it('should detect white field message',() => {
fixture.whenStable().then(() => {
inputEmail.value = faker.internet.email();
inputEmail.dispatchEvent(new Event('input'));
inputPassword.value = faker.internet.password();
inputPassword.dispatchEvent(new Event('input'));
loginButton.click();
fixture.detectChanges();
});
});
});
我可能做错了什么?
编辑1 :我非常确定永远不会执行subscribe
方法,因为在subscribe方法中,如果有一个名为token.error
的节点,组件应设置要在此块中显示的错误变量:
<div class="error-block" *ngIf="error">
<h6>{{ error }}</h6>
</div>
但这条信息永远不会出现在因果报应中。
编辑2 : 这是我模拟原始身份验证服务的mockService:
import { Injectable } from '@angular/core';
import { Credential } from '../models/credential';
import { AuthenticationService } from './authentication.service'
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import * as token from 'rand-token';
@Injectable()
export class TokenManagerServiceMock {
private tokenKey: string = 'codeToken';
private subject = new Subject<any>();
constructor() {}
public generateNewToken(credential: Credential): void {
let currentTime:number = (new Date()).getTime();
if (credential.email == 'eminetto@coderockr.com') {
const data = token.suid(24);
this.subject.next(JSON.stringify({ttl: currentTime, data}));
} else {
const error = 'Usuário ou senha inválidos';
this.subject.next(JSON.stringify({ttl: currentTime, error}));
}
}
public retrieveToken(): Observable<string> {
console.log('achieved retrieve');
return this.subject.asObservable();
}
}
答案 0 :(得分:1)
测试中问题的根本原因是模拟类中的主题是使用rxjs Subject
类设置的。您的使用情况表明您应该实际使用BehaviorSubject
类,以便主题的任何后期订阅者(在将值推送到主题后进行订阅)可以在订阅时获得最新值。
通过了解模拟服务中发生的事情,我们可以看到我们有一个迟到的用户:
generateNewToken(credential: Credential): void {
...
this.subject.next(...);
...
}
retrieveToken(): Observable<string> {
return this.subject.asObservable();
}
您可以在以下模拟的使用中看到此行为:
this.tokenManagerService.generateNewToken(this.credential);
this.tokenManagerService.retrieveToken().subscribe(...);
首先,我们生成一个新令牌并将其推送到主题上,返回到组件,最后订阅主题。通过切换到BehaviorSubject
,我们可以毫不费力地让迟到的订阅者看不到从调用推送的值以生成新令牌。