簡體   English   中英

如何在 Angular 中模擬 Rxjs 主題

[英]How to mock Rxjs Subject in Angular

我有一個集中的 DataStore 來保存我的報表連接。 這樣我就可以管理報告連接事件,如 onShow、onError 和 onCancel; 所以實施者不必這樣做。 無論如何。 如何模擬SomeService.doSomething function 以便它返回我的連接 object 並且它們在 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
    })
  }
}

嘗試借助 mocking 庫來簡化測試。 例如,如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);
  });
});

我在其他地方回答了類似的問題。 請檢查測試用例的這種模擬數據方式:

// 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