【问题标题】:AngularCli - Subscribe is not being executed inside onSubmit during testAngular Cli - 在测试期间订阅未在 onSubmit 内部执行
【发布时间】:2018-04-24 19:58:42
【问题描述】:

我正在尝试测试一个基本上具有电子邮件和密码字段的组件。一旦填写了这两个字段,用户就可以单击按钮并向服务器发送请求。当我通过浏览器测试这个组件时,它会执行我想要的行为。但是当我使用规范文件测试组件时,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();
  }
}

这是规范文件和测试本身的配置:

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();
    });
  });
});

我可能做错了什么?

EDIT 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();
  }
}

【问题讨论】:

  • 请提供您在测试中调试的细节'should detect white field message',以便其他人可以尝试提供帮助。 (例如,您如何知道它没有执行订阅等)
  • @DanielWSrimpel 那里有一个console.log 并且永远不会被执行
  • 再次,请提供详细信息,准确说明您遇到的情况以及到目前为止您为调试问题所做的工作(在您的问题中提供)
  • 您能否提供有关您的TokenManagerServiceMock 类的详细信息,以显示它如何模拟在测试中的类(AuthenticationComponent)中使用的方法(generateNewToken(...)retrieveToken(...))?
  • 尝试在模拟类中将 Subject 更改为 BehaviorSubject

标签: angular angular-cli karma-jasmine


【解决方案1】:

您的测试中问题的根本原因是您的模拟类中的主题是使用 rxjs Subject 类设置的。您的用法表明您应该实际使用BehaviorSubject 类,以便该主题的任何后期订阅者(在将值推送到主题后订阅)在订阅时可以获得最新的值。

通过了解模拟服务中发生的情况,我们可以看到我们有一个迟到的订阅者:

generateNewToken(credential: Credential): void {
  ...
  this.subject.next(...);
  ...
}

retrieveToken(): Observable<string> {
  return this.subject.asObservable();
}

您可以在下面的 mock 使用中看到这种行为:

this.tokenManagerService.generateNewToken(this.credential);
this.tokenManagerService.retrieveToken().subscribe(...);

首先,我们生成一个新令牌并将其推送到主题上,然后返回组件,最后订阅主题。通过切换到BehaviorSubject,我们不会遇到迟到的订阅者看不到从调用中推送的值以生成新令牌的问题。

【讨论】:

    猜你喜欢
    • 2018-09-29
    • 1970-01-01
    • 2019-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-10
    • 1970-01-01
    • 2019-03-08
    相关资源
    最近更新 更多