测试订阅了一个可观察的方法 - Angular 2

时间:2016-02-26 18:20:02

标签: jasmine angular rxjs

我想测试一个Angular 2组件内部的方法,该组件订阅了从服务中的方法返回的observable。以下是摘要中服务方法的代码:

public create(user: User): Observable<any> {
  return this.http.post(this._api.create,
    JSON.stringify(user), {
      headers: this.apiConfig.getApiHeaders()
    }).map((res: Response) => res.json());
  }

单元测试此方法很容易,因为它返回一个observable,所以我可以订阅它。但是我想测试已经订阅它的组件中的方法:

public onSubmit(user: User): void {
  this._authentication.create(user).subscribe((token) => {
    localStorage.setItem('token', token);
    this.router.navigate(['/Home']);
  });
}

到目前为止我的规格是什么,但是当我试图窥探localStorage.setItem时,它会回来,因为没有被调用。我的理解是它可能会检查它是否在被实际调用之前被调用过。

it('Should login a user and on success store a token in localStorage',
  injectAsync([TestComponentBuilder], (tcb) => {
    return tcb.createAsync(Login).then((fixture) => {
      let instance = fixture.debugElement.componentInstance;
      localStorage.clear();
      spyOn(localStorage, 'setItem');
      instance.onSubmit({userId: 'some@email.com', password: 'password', siteName: 'sample'});
      expect(localStorage.setItem).toHaveBeenCalled();
    });
  })
);

我想知道我是否需要模拟this._authentication.create方法来返回一个带有模拟响应的新observable?

经过更多研究后,一些文章表明我确实需要模拟服务并返回一个同步运行以解决问题的Observable.of(),复制下面的代码。然而,这仍然不起作用,我一直在努力这一天,我不觉得这应该是那么难,任何帮助赞赏。

class MockAuthentication extends Authentication {
  public create(user: Object): Observable<any> {
    return Observable.of({'test': 'test'});
  }
}

3 个答案:

答案 0 :(得分:3)

好的,所以它把我当天的大部分时间都带走了,但我终于破解了它。而不是使用injectAsync和TestComponentBuilder来设置规范我只需要使用注入和注入组件就像你做一个服务。这看起来很好,因为我不需要在视图中测试任何事件。

这是最终确实有效的规范:

it('Should set token in localStorage, set the new user, 
and navigate to home page on succesful login', 
  inject([Login], (login) => {
    login.router.config([ { path: '/', name: 'Home', component: Home }]);
    spyOn(localStorage, 'setItem');
    spyOn(login._currentUser, 'set');
    spyOn(login.router, 'navigate');
    login.onSubmit({ userId: 'some@email.com', password: 'password', siteName: 'sample' });
    expect(localStorage.setItem).toHaveBeenCalledWith('token', 'newToken');
    expect(login._currentUser.set).toHaveBeenCalledWith({ 'test': 'one' });
    expect(login.router.navigate).toHaveBeenCalledWith(['/Home']);
}));

希望这有助于将来的某些人。

答案 1 :(得分:1)

我想你想向你的组件注入一个模拟Router实例,然后在模拟navigate(['/Home'])上调用Router之后,检查是否调用了localStorage.setItem(...)

答案 2 :(得分:-1)

See my gist here

基本上你可以在这里做几件事。首先,使用您想要的令牌(或其他响应)的简单可观察响应来存储您的http调用(我从服务中猜测)。

service.stub.ts

export class MyStub {
  public create(user: User): Observable<User> {
    return Observable.of('insert test token here');
  }
// other stubbed methods ...
}

然后在你的测试中:

myComp.spec.ts

let comp: MyComponent;
let fixture: ComponentFixture<MyComponent>;
let sst: ServiceStub;
describe('MyComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [MyComponent],
      schemas: [NO_ERRORS_SCHEMA]
    }).overrideComponent(OnboardFacilityNewComponent, {
      set: {
        providers: [
          { provide: MyService, useClass: ServiceStub },
        ]
      }
    })
    .compileComponents()
      .then(() => {
        fixture = TestBed.createComponent(MyComponent);
        comp = fixture.componentInstance;
        st = fixture.debugElement.injector.get(MyService);
      });
  }));
  it('should submit new onboardFacility', fakeAsync(() => {
    const sst = spyOn(sst, 'create').and.returnValue(
      Observable.of('some token here')
    );
    comp.onSubmit(testUser);
    fixture.detectChanges();
    expect(comp.token).toEqual('some token here');
    expect(spy.calls.any()).toEqual(true);
  }));
});

在这里,您可以简单地用测试数据替换实际数据来测试您的测试行为,而不是测试平台,您的服务,localStorage等。显然,我在这里写的测试假设您将存储从您的服务返回的令牌在你的组件中,而不是localStorage (though there is a way to do that),但我只是简单地展示概念而不是你的具体用例。

在您的使用案例中,您还需要存根路由器,您可以学习如何做here.