2016-02-26 140 views
7

我想测试订阅从服务中的方法返回的observable的Angular 2组件中的方法。下面是在总结服务方法的代码:测试订阅observable的方法 - Angular 2

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

很容易单元测试这种方法,因为它返回一个可观察的,所以我可以只订阅。但我想测试的方法在已经订阅了这个组件:

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

我的继承人规范,到目前为止,但是当我尝试spyOn的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: '[email protected]', password: 'password', siteName: 'sample'}); 
     expect(localStorage.setItem).toHaveBeenCalled(); 
    }); 
    }) 
); 

我想知道如果我需要模拟出this._authentication.create方法返回一个新的观察到的,在这一个模拟的反应?

经过更多的研究,一些文章指出我需要嘲笑服务并返回一个同步运行的Observable.of()来解决问题,请仔细复制下面的代码。然而,这仍然不起作用,我一直在这一天的大部分时间里工作,我不觉得这应该是那么辛苦,任何帮助表示赞赏。

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

回答

2

好的,这是我一天中的大部分时间,但我终于破解了它。而不是使用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: '[email protected]', password: 'password', siteName: 'sample' }); 
    expect(localStorage.setItem).toHaveBeenCalledWith('token', 'newToken'); 
    expect(login._currentUser.set).toHaveBeenCalledWith({ 'test': 'one' }); 
    expect(login.router.navigate).toHaveBeenCalledWith(['/Home']); 
})); 

希望这可以帮助别人的未来。

1

我猜你想要一个模拟Router实例注入到你的组件,然后navigate(['/Home'])后,叫上模拟Router,你是否localStorage.setItem(...)被调用。

+0

感觉就像检查会发生,他们接到电话,因为之前的认购,但我是新观察者。此外,我们以前只是在Angular 1中使用spyOn(localStorage,'setItem'),但我很难搞清楚如何对Angular 2做同样的处理。 – HomeBrew

+0

我的不好,看起来你仍然使用spyOn相同,我的问题是我无法弄清楚你需要从哪里导入'spyOn',但似乎你只是拥有它。但是,似乎我之前对支票发生的陈述仍然正确。我已经更新了我的问题以包含规范。 – HomeBrew

-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等等。显然,我在这里写的测试假设你会将你的服务返回的令牌存储在你的组件中,r ather然后localStorage (though there is a way to do that),但我只是简单地显示的概念,而不是你的具体用例。

在您的使用情况下,你还需要存根路由器,你可以学习如何做here.

+0

这是没有道理的。你为什么在服务模拟中调用spyOn?如果它被嘲笑,它也返回一个模拟值,你不需要用returnValue Jasmine函数返回另一个模拟值。 –

+0

@EliasGarcia因为您正在确定函数逻辑是否调用依赖关系。间谍是可选的。 –