简体   繁体   English

如何测试 Angular Jasmine Spec 中的 subject 和 observable 返回值?

[英]How to test a subject and observable return value in Angular Jasmine Spec?

In Angular 9, I have a loader service like below.在 Angular 9 中,我有一个如下所示的加载器服务。

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

export interface LoaderState {
  show: boolean;
}

@Injectable({
  providedIn: 'root'
})
export class LoaderService {

  private loaderSubject = new Subject<LoaderState>();

  loaderState = this.loaderSubject.asObservable();

  constructor() { }

  show() {
      this.loaderSubject.next({show: true} as LoaderState);
  }

  hide() {
      this.loaderSubject.next({show: false} as LoaderState);
  }
}

I want to test the show() and hide() methods.我想测试show()hide()方法。 For that, I have written a spec like below.为此,我编写了如下规范。

it('should show', (done) => {
      spyOn(loaderService, 'show').and.callThrough();
      loaderService.show();
      expect(loaderService.show).toHaveBeenCalled();
      loaderService.loaderState.subscribe((state) => {
          expect(state).toBe({show: true});
          done();
      });
  });

But I get below error when I run this但是当我运行这个时我得到了以下错误

Chrome 86.0.4240 (Windows 10.0.0) LoaderService should show FAILED Error: Timeout - Async callback was not invoked within 5000ms (set by jasmine.DEFAULT_TIMEOUT_INTERVAL) Chrome 86.0.4240 (Windows 10.0.0) LoaderService 应显示 FAILED 错误:超时 - 异步回调未在 5000 毫秒内调用(由 jasmine.DEFAULT_TIMEOUT_INTERVAL 设置)

I have searched a lot before asking this question.在问这个问题之前,我已经搜索了很多。 But can not seem to find the right solution.但似乎无法找到合适的解决方案。 I am new to Jasmin Unit testing.我是 Jasmin 单元测试的新手。 Any help is appreciated.任何帮助表示赞赏。

Edit: Posting complete spec file for reference.编辑:发布完整的规范文件以供参考。

import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { LoaderComponent } from './loader.component';
import { LoaderService } from './loader.service';

describe('LoaderService', () => {
  let component: LoaderComponent;
  let fixture: ComponentFixture<LoaderComponent>;
  let loaderService: LoaderService;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ LoaderComponent ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(LoaderComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
    loaderService = TestBed.inject(LoaderService);
  });

  it('should be create', () => {
    expect(loaderService).toBeTruthy();
  });

  it('should show', (done) => {
      spyOn(loaderService, 'show').and.callThrough();
      loaderService.show();
      expect(loaderService.show).toHaveBeenCalled();
      loaderService.loaderState.subscribe((state) => {
          expect(state).toBe({show: true});
          done();
      });
  });
});

The problem is that you're calling subscribe after the actual method call.问题是您在实际方法调用之后调用订阅。 So when you call show();所以当你调用show(); there is nothing subscribed to that event, and since the done() callback is inside of it... it's never called.没有任何人订阅该事件,并且由于 done() 回调在其中......它从未被调用过。

it('should show', (done) => {
    service.loaderState.subscribe((state) => { //subscribe first
      expect(state).toEqual({show: true}); // change toEqual instead of toBe since you're comparing objects
      done();
      });

    spyOn(service, 'show').and.callThrough();
    service.show(); // invoke after
    expect(service.show).toHaveBeenCalled();
  });

Here's my solution to this pattern:这是我对此模式的解决方案:

Service Class服务 Class

import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';

@Injectable({

  providedIn: 'root'

})

export class MyService {

  private myServiceEvent = new Subject<any>();

  constructor() { }

  success(): Observable<any> {
    return this.myServiceEvent.asObservable();
  }
  
  doSomething(someData: any): void {
    // stuff
    this.myServiceEvent.next(someData);
  }
}

Component that uses the service (the observable would be triggered by some other component or service calling the doSomething method on the service)使用该服务的组件(可观察对象将由其他组件或服务调用服务上的doSomething方法触发)

import { Component, OnInit } from '@angular/core';
import { MyService } from ./my-service.ts

@Component({
  selector: 'app-mycomponent',
  templateUrl: './app-mycomponent.component.html',
  styleUrls: ['./app-mycomponent.component.scss']
})
export class AppMyComponent implements OnInit {

    private test = '';

  constructor(
    private myService: MyService,
  ) {  }

  ngOnInit() {
    this.myService.success().subscribe(data => {
        test = data;
    });
  }
}

Spec that tests the component using the service's Observable response.使用服务的 Observable 响应测试组件的规范。 Note the construction of the service mock and how the Subject variable is used to trigger the Observable.请注意服务模拟的构造以及如何使用 Subject 变量来触发 Observable。

import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core';
import { waitForAsync, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { Observable, Subject } from 'rxjs';

import { AppMyComponent } from './app-my.component';
import { MyService } from ./my-service.ts

describe('AppMyComponent', () => {
  let component: AppMyComponent;
  let fixture: ComponentFixture<AppMyComponent>;

  const myServiceSubject = new Subject<any>();

  const myServiceMock = jasmine.createSpyObj('MyService', [], {
    success: () => myServiceSubject.asObservable()
  });
  
  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      schemas: [ CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA],
      declarations: [ AppMyComponent ],
      providers: [
        { provide: MyService, useValue: myServiceMock }
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(AppComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should execute subscription to MyService success event', fakeAsync(() => {
    myServiceSubject.next('somedata');
    tick();
    expect(componenet.test).toEqual('somedata');
  }));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM