【问题标题】:How to mock Rxjs Subject in Angular如何在 Angular 中模拟 Rxjs 主题
【发布时间】:2021-05-28 23:21:45
【问题描述】:

我有一个集中的 DataStore 来保存我的报表连接。这样我就可以管理报告连接事件,如 onShow、onError 和 onCancel;所以实施者不必这样做。无论如何。如何模拟 SomeService.doSomething 函数,以便它返回我的连接对象,并且它们在 onShow 主题上发出。请看我的should resolve test data 测试。我怎样才能适当地模拟这个。

mockSomeService.doSomething.and.callFake(() => {
  const response = new ReportManagerConnection();
  response.onShow.next({ data: [ {id: 1} ]})
  return response
})

这是我的测试。

describe('SomeComponent', () => {
  let component: SomeComponent;
  let fixture: ComponentFixture<SomeComponent>;
  beforeEach(async(() => {
    const mockSomeService: jasmine.SpyObj<SomeService> = jasmine.createSpyObj<SomeService>(
      'SomeService',
      ['doSomething']
    );
    mockSomeService.doSomething.and.callFake(() => {
      const response = new ReportManagerConnection();
      response.onShow.next({ data: [ {id: 1} ]})
      return response
    })
    TestBed.configureTestingModule({
      imports: [ ],
      declarations: [SomeComponent],
      providers: [
        { provide: SomeService, useValue: mockSomeService },
      ]
    }).compileComponents();
  }));
  beforeEach(() => {
    fixture = TestBed.createComponent(SomeComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });
  it('should resolve test data', fakeAsync(() => {
    component.loadData()
    tick()
    expect(component.data.length).toBeGreaterThan(0)
  }));
});

这是我的代码

export class ReportManagerConnection {
  onShow: Subject<any>
  onFinish: Subject<any>
  onCancel: Subject<any>
  onShow: Subject<any>
  onStart: ReplaySubject<any>
  onError: ReplaySubject<any>
  constructor() {
    this.onFinish = new Subject()
    this.onCancel = new Subject()
    this.onShow = new Subject()
    this.onStart = new ReplaySubject()
    this.onError = new ReplaySubject()
  }
}

@Injectable({
    providedIn: 'root'
})
export class ReportStoreService {
  connections: Array<ReportManagerConnection> = []
  constructor( public http: HttpClient ) { }
  send(){
    const connection = new ReportManagerConnection()
    connections.push(connection)
    connection.onShow.asObservable().subscribe((c)=>{
        alert('Report Shown')
    })
    return connection
  }
}

@Injectable()
export class SomeService {
  constructor( public http: HttpClient, public _ReportStoreService: ReportStoreService ) { }
  doSomething(){
    const connection = this._ReportStoreService.send()
    this.http.get('/api/test').subscribe(c=>{
        connection.onShow.next({ data: c })
    })
    return connection
  }
}

@Component({
  selector: 'some-component',
  templateUrl: './some-component.component.html',
})
export class SomeComponent {
  public data = []
  constructor(public _SomeService: SomeService) {}
  loadData(){
    const connection = _SomeService.doSomething()
    connection.onShow.asObservable().subscribe((c)=>{
        this.data = c.data
    })
  }
}

【问题讨论】:

    标签: angular angular-unit-test


    【解决方案1】:

    我在其他地方回答了类似的问题。请检查测试用例的这种模拟数据方式:

    // In your component.spec file, 
    
    @Injectable()
    class MockService extends RealService {
      yourOriginalServiceMethod() {
        return of(mockData);
        // Here mockData can be any mocked-data. It should be of whatever the type your 
           original method in the service returns. Like an object 
       // 'of' is the RXJS operator. It will turn your mockData to an Observable so that when you run the test case, it will be subscribed without any issue. 
      }
    }
      
    beforeEach(() => {
      fixture = TestBed.createComponent(AppComponent);
      component = fixture.componentInstance;
    
      realService = TestBed.get(RealService); // this is important to test the subscription error scenario
    });
    
    describe('AppComponent', () => { // 2
      beforeEach(async(() => { // 3
        TestBed.configureTestingModule({
          declarations: [
            AppComponent
          ],
          providers: [
            {
               provide: MockService,
               useClass: RealService
            }
          ]
        }).compileComponents();
      }));
    
    // Now your test case,  
    
    it("component #yourComponentMethod() method for successful subscription",() => {
      spyOn(component, "yourComponentMethod").and.callThrough();
      component.yourComponentMethod();
      expect(component.yourComponentMethod).toHaveBeenCalled();
    
      // THis method will clear the successful subscription scenario
    });
    
    
    it("component #yourComponentMethod() method for failed subscription",() => {
    
      // This line will call the service and instead of returning mockData, it will fail it.
      spyOn(realService, 'yourMethodName').and.returnValue(throwError({status: 500}));
      
      // Rest is the same
      spyOn(component, "yourComponentMethod").and.callThrough();
      component.yourComponentMethod();
      expect(component.yourComponentMethod).toHaveBeenCalled();
    
      // THis method will clear the failed subscription scenario
    });
    

    【讨论】:

      【解决方案2】:

      尝试借助模拟库来简化测试。 比如ng-mocks

      带有解决方案的沙盒:https://codesandbox.io/s/ecstatic-cache-lv8gk?file=/src/test.spec.ts

      describe('SomeComponent', () => {
        // configuring TestBed with one line.
        beforeEach(() => MockBuilder(SomeComponent).mock(SomeService));
      
        it('should resolve test data', () => {
          // creating a mock instance of ReportManagerConnection
          // with a preconfigured emit on subscribe to onShow
          const reportManagerConnection = MockService(ReportManagerConnection, {
            onShow: new BehaviorSubject({ data: [ {id: 1} ]}),
          });
      
          // customizing a mock instance of SomeService
          MockInstance(SomeService, 'doSomething', () => reportManagerConnection);
      
          // rendering the component
          const component = MockRender(SomeComponent).point.componentInstance;
      
          // asserting
          expect(component.data.length).toEqual(0);
          component.loadData();
          expect(component.data.length).toBeGreaterThan(0);
        });
      });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-12-26
        • 2019-10-16
        • 2021-12-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多