Testing a method subscribed to observable - Angular 2

I want to test a method inside an Angular 2 component that subscribes to an observable that returns from a method in the service. Below is the code for the service method:

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

It is easy to unit test this method because it returns the observable, so I can just subscribe to it. But I want to test a method in a component that has already subscribed to this:

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

Here is my specification so far, but when I try to look into localStorage.setItem, it returns as not being called. I understand that he probably checks to see if he was called before his actual call.

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

I am wondering if I need to catch this._authentication.create method in order to return a new observable using a mock response?

After several studies, several articles showed that I need to excuse the service and return Observable.of (), which works synchronously to solve the problem, copy the code below. It still doesn’t work, I work on it most of the day, I don’t feel that it should be so difficult, any help was appreciated.

 class MockAuthentication extends Authentication { public create(user: Object): Observable<any> { return Observable.of({'test': 'test'}); } } 
+7
angular jasmine rxjs
source share
3 answers

Okay, so it took me most of the day, but I finally cracked it. Instead of using injectAsync and TestComponentBuilder to set up the specification, I just need to use injection and insert the component the same way you do the service. This seems fine because I don’t need to check anything in the view, such as events.

Here is the final specification that works:

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

Hope this can help someone in the future.

+2
source share

I assume that you want to add an instance of mock Router to your component, and then after navigate(['/Home']) was called in mock Router , you check if localStorage.setItem(...) was called.

+1
source share

See my meaning here .

Basically, you can do a few things here. First of all, drown out your HTTP call (I guess from the service) with the simple observable token (or other answer) answer you want.

service.stub.ts

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

And then inside your test:

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

Here you can simply replace the actual data with test data to check the behavior of your testing, and not your test stand, your services, localStorage, etc. Obviously, the test I wrote here assumes that you would save the token returned from your service in your component, not localStorage (although there is a way to do this) , but I just want to show the concept, not your specific use case .

In your case, you will also need to jam the router, which you can learn how to do here.

-one
source share

All Articles