简体   繁体   English

使用 Jasmine 使用 rxjs 主题对 Angular 组件进行单元测试

[英]Unit Testing Angular components with rxjs Subjects using Jasmine

I have two components and a service layer.我有两个组件和一个服务层。 The service layer has a subject declared in it with a set and get method definition.服务层在其中声明了一个带有 set 和 get 方法定义的主题。 The first component, based on an user action calls the set method of the subject while the second component is subscribed to the same subject.第一个组件基于用户操作调用主题的 set 方法,而第二个组件订阅相同的主题。 It works just fine but when I try to unit test the subscription in second component, I'm having trouble with the approach.它工作得很好,但是当我尝试在第二个组件中对订阅进行单元测试时,我在使用该方法时遇到了问题。 I have a mock service layer created to test other functionalities as well.我创建了一个模拟服务层来测试其他功能。

The subscription inside ngOnit is not getting hit during the test run. ngOnit 中的订阅在测试运行期间没有受到影响。 Code executes within the subscribe when component initializes but not after setSubject is invoked.代码在组件初始化时在订阅内执行,但不在调用 setSubject 之后执行。 When I debug, I can see that it hits after the execution is complete but I have no way to verify if data is updated.当我调试时,我可以看到它在执行完成后命中,但我无法验证数据是否更新。

export class MockService {
 private subject = new Subject<any>();

 getSubject() {
   return Observable.of(this.subject);
 }

 setSubject(value) {
   this.subject.next({ message: value });
 }

}

Component 2组件 2

export class Component2 implements OnInit {
  textFromComponent1: string;
  subscription: Subscription;

  constructor(public service: Service) {}
  ngOnInit() {
    this.subscription = this.service.getSubject().subscribe(message => {
      this.textFromComponent1 = message.value;
    });
  }
}

Component 2 Unit Test组件 2 单元测试

beforeEach( async(() => {
    TestBed.configureTestingModule( {
        declarations: [Component2],
        schemas: [CUSTOM_ELEMENTS_SCHEMA],
        providers: [{ provide: Service, useClass: MockService }]
    } )
        .compileComponents();
} ) );
beforeEach(() => {
    fixture = TestBed.createComponent( Component2 );
    component = fixture.componentInstance;
    fixture.detectChanges();
});
it('should test subscription' () => {
   component.service.setSubject("abc");
   expect(component.textFromComponent1).toBe("abc");
});

Since a Subject is a special type of Observable , so you should return this.subject from the getSubject() method without wrapping it to an observable using the Observable.of() method.由于Subject是一种特殊类型的Observable ,因此您应该从getSubject()方法返回this.subject ,而不是使用Observable.of()方法将其包装到observable

Here is a working example using angular v11+:这是一个使用angular v11+ 的工作示例:

example.component.ts : example.component.ts

import { Component, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { Service } from './example.service';

@Component({})
export class Component2 implements OnInit {
  textFromComponent1: string;
  subscription: Subscription;

  constructor(public service: Service) {}
  ngOnInit() {
    console.log('ngOnInit');
    this.subscription = this.service.getSubject().subscribe((data) => {
      console.log(data);
      this.textFromComponent1 = data.message;
    });
  }
}

example.service.ts : example.service.ts

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

@Injectable()
export class Service {
  private subject = new Subject<{ message: string }>();

  getSubject() {
    return this.subject;
  }

  setSubject(value: string) {
    this.subject.next({ message: value });
  }
}

example.component.spec.ts : example.component.spec.ts

import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { Subject } from 'rxjs';
import { Component2 } from './example.component';
import { Service } from './example.service';

class MockService {
  private subject = new Subject<any>();

  getSubject() {
    return this.subject;
  }

  setSubject(value: string) {
    this.subject.next({ message: value });
  }
}

fdescribe('52263178', () => {
  let fixture: ComponentFixture<Component2>;
  let component: Component2;
  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        declarations: [Component2],
        schemas: [CUSTOM_ELEMENTS_SCHEMA],
        providers: [{ provide: Service, useClass: MockService }],
      }).compileComponents();
    })
  );
  beforeEach(() => {
    fixture = TestBed.createComponent(Component2);
    component = fixture.componentInstance;
  });

  it(
    'should test subscription',
    waitForAsync(() => {
      expect(component.textFromComponent1).toBeUndefined();
      fixture.detectChanges();
      component.service.setSubject('abc');
      fixture.whenStable().then(() => {
        expect(component.textFromComponent1).toBe('abc');
      });
    })
  );
});

unit test result:单元测试结果:

✔ Browser application bundle generation complete.
LOG: 'ngOnInit'
Chrome Headless 80.0.3987.87 (Mac OS 10.13.6): Executed 0 of 33 SUCCESS (0 secs / 0 secs)
LOG: Object{message: 'abc'}
Chrome Headless 80.0.3987.87 (Mac OS 10.13.6): Executed 0 of 33 SUCCESS (0 secs / 0 secs)
Chrome Headless 80.0.3987.87 (Mac OS 10.13.6): Executed 2 of 33 (skipped 31) SUCCESS (0.152 secs / 0.071 secs)
TOTAL: 2 SUCCESS

test coverage:测试覆盖:

在此处输入图片说明

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

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