简体   繁体   English

如何在 Angular 中模拟 Rxjs 主题

[英]How to mock Rxjs Subject in Angular

I have a centralized DataStore to hold my report connections.我有一个集中的 DataStore 来保存我的报表连接。 So that I can manage report connections events like onShow, onError and onCancel;这样我就可以管理报告连接事件,如 onShow、onError 和 onCancel; so the implementer doesn't have to.所以实施者不必这样做。 Anyways.无论如何。 How can I mock the SomeService.doSomething function so that it returns my connection object and them emits on the onShow Subject.如何模拟SomeService.doSomething function 以便它返回我的连接 object 并且它们在 onShow 主题上发出。 Please look at my should resolve test data test.请看我的should resolve test data测试。 How can I mock this appropriately.我怎样才能适当地嘲笑这个。

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

Here is my test.这是我的测试。

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

Here is my code这是我的代码

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

Try to simplify the test with a help of a mocking library.尝试借助 mocking 库来简化测试。 For example, such as ng-mocks .例如,如ng-mocks

a sandbox with the solution: https://codesandbox.io/s/ecstatic-cache-lv8gk?file=/src/test.spec.ts带有解决方案的沙箱: 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);
  });
});

I answered a similar question else-where.我在其他地方回答了类似的问题。 Please check this way of mocking-data for the test cases:请检查测试用例的这种模拟数据方式:

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

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

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