Reputation: 24679
I am trying to unit test a angular 2 component and simulate an error (400/bad request) from the http backend.
Here is my code:
describe('Component: UserAccountActivationComponent', () => {
let fixture: ComponentFixture<UserAccountActivationComponent>;
let userAccountActivationComponent: UserAccountActivationComponent;
const observableMock = Observable.of('Some Observable');
beforeEach(async(() => {
TestBed.configureTestingModule({
providers: [
MockBackend,
BaseRequestOptions,
{
provide: Http,
useFactory: (backend, options) => new Http(backend, options),
deps: [MockBackend, BaseRequestOptions]
},
{
provide: ActivatedRoute, useClass: RouteMock
},
],
imports: [AppModule, HttpModule]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(UserAccountActivationComponent);
userAccountActivationComponent = fixture.componentInstance;
});
it('should not signin user nor navigate to dashboard if account is not activated', fakeAsync(inject(
[UserAccountService, SessionSigninService, Router, MockBackend],
(userAccountService: UserAccountService, signinService: SessionSigninService, router: Router, mockBackend: MockBackend) => {
const opts = {
type: ResponseType.Error,
status: 400
};
const responseOpts = new ResponseOptions(opts);
mockBackend.connections.subscribe(
(connection: MockConnection) => {
connection.mockError(new MockError(responseOpts));
});
const activateSpy = spyOn(userAccountService, 'activateAccount').and.callThrough();
const signinSpy = spyOn(signinService, 'signinByUserAccountToken').and.returnValue(observableMock);
const navigateSpy = spyOn(router, 'navigate').and.returnValue(observableMock);
fixture.detectChanges();
tick();
expect(activateSpy).toHaveBeenCalled();
expect(signinSpy).not.toHaveBeenCalled();
expect(navigateSpy).not.toHaveBeenCalled();
})));
});
class MockError extends Response implements Error {
name: any;
message: any;
}
class RouteMock {
readonly params = Observable.of({userAccountToken: 'a-token'});
}
I am simulating an error occurring in the userAccountService.activateAccount
method. I just want to check that neither signinService.signinByUserAccountToken
nor the router.navigate
are called. This is what I am trying to do with the jasmine expectations:
expect(activateSpy).toHaveBeenCalled();
expect(signinSpy).not.toHaveBeenCalled();
expect(navigateSpy).not.toHaveBeenCalled();
However, I the test is stopped by the simulated error:
Chrome 56.0.2924 (Mac OS X 10.12.3) Component: UserAccountActivationComponent should not signin user nor navigate to dashboard if account is not activated FAILED
Error: Error in :0:0 caused by: Response with status: 400 null for URL: null
at ViewWrappedError.ZoneAwareError (webpack:///~/zone.js/dist/zone.js:811:0 <- src/test.ts:142448:33)
at ViewWrappedError.BaseError [as constructor] (webpack:///~/@angular/core/src/facade/errors.js:22:0 <- src/test.ts:36304:16)
at ViewWrappedError.WrappedError [as constructor] (webpack:///~/@angular/core/src/facade/errors.js:87:0 <- src/test.ts:36369:16)
at new ViewWrappedError (webpack:///~/@angular/core/src/linker/errors.js:77:0 <- src/test.ts:70405:16)
at proxyClass.DebugAppView._rethrowWithContext (webpack:///~/@angular/core/src/linker/view.js:653:0 <- src/test.ts:111685:23)
at proxyClass.DebugAppView.detectChanges (webpack:///~/@angular/core/src/linker/view.js:626:0 <- src/test.ts:111658:18)
at ViewRef_.detectChanges (webpack:///~/@angular/core/src/linker/view_ref.js:170:0 <- src/test.ts:71336:20)
at ComponentFixture._tick (webpack:///~/@angular/core/bundles/core-testing.umd.js:196:0 <- src/test.ts:15653:36)
at webpack:///~/@angular/core/bundles/core-testing.umd.js:210:45 <- src/test.ts:15667:53
at ZoneDelegate.invoke (webpack:///~/zone.js/dist/zone.js:242:0 <- src/test.ts:141879:26)
at ProxyZoneSpec.onInvoke (webpack:///~/zone.js/dist/proxy.js:79:0 <- src/test.ts:98996:39)
at ZoneDelegate.invoke (webpack:///~/zone.js/dist/zone.js:241:0 <- src/test.ts:141878:32)
at Object.onInvoke (webpack:///~/@angular/core/src/zone/ng_zone.js:271:0 <- src/test.ts:38049:37)
at ZoneDelegate.invoke (webpack:///~/zone.js/dist/zone.js:241:0 <- src/test.ts:141878:32)
at Zone.run (webpack:///~/zone.js/dist/zone.js:113:0 <- src/test.ts:141750:43)
FYI, here is the component under test:
@Component({
templateUrl: './useraccount-activation.component.html'
})
export class UserAccountActivationComponent implements OnInit {
constructor(private userAccountService: UserAccountService,
private signinService: SessionSigninService,
private router: Router,
private route: ActivatedRoute) {
}
ngOnInit() {
this.route.params
.take(1)
.pluck('userAccountToken')
.switchMap((userAccountToken: string) =>
this.userAccountService.activateAccount(userAccountToken)
.switchMapTo(this.signinService.signinByUserAccountToken(userAccountToken))
)
.subscribe(() => this.router.navigate(['/dashboard']));
}
}
edit 1: Unfortunately, changing to:
mockBackend.connections.subscribe(
(connection: MockConnection) => {
responseOpts.url = connection.request.url;
connection.mockError(new MockError(responseOpts));
});
Still causes the following error:
'HttpClient error: ', MockError{_body: null, status: 400, ok: false, statusText: null, headers: null, type: 3, url: '/api/useraccount/activate/a-valid-token'}
Error: Error in :0:0 caused by: Response with status: 400 null for URL: /api/useraccount/activate/a-valid-token
at Error.ZoneAwareError (webpack:///~/zone.js/dist/zone.js:958:0 <- src/test.ts:143513:33)
at ZoneAwareError (webpack:///~/zone.js/dist/zone.js:955:0 <- src/test.ts:143510:35)
at wrappedError (webpack:///~/@angular/core/src/error_handler.js:144:21 <- src/test.ts:36697:34)
at viewWrappedError (webpack:///~/@angular/core/src/linker/errors.js:65:21 <- src/test.ts:70785:125)
at proxyClass.DebugAppView._rethrowWithContext (webpack:///~/@angular/core/src/linker/view.js:656:0 <- src/test.ts:112336:111)
at proxyClass.DebugAppView.detectChanges (webpack:///~/@angular/core/src/linker/view.js:629:0 <- src/test.ts:112309:18)
at ViewRef_.detectChanges (webpack:///~/@angular/core/src/linker/view_ref.js:172:0 <- src/test.ts:71715:20)
at ComponentFixture._tick (webpack:///~/@angular/core/bundles/core-testing.umd.js:196:0 <- src/test.ts:15805:36)
at webpack:///~/@angular/core/bundles/core-testing.umd.js:210:45 <- src/test.ts:15819:53
at ZoneDelegate.invoke (webpack:///~/zone.js/dist/zone.js:330:0 <- src/test.ts:142885:26)
at ProxyZoneSpec.onInvoke (webpack:///~/zone.js/dist/proxy.js:79:0 <- src/test.ts:99788:39)
at ZoneDelegate.invoke (webpack:///~/zone.js/dist/zone.js:329:0 <- src/test.ts:142884:32)
at Object.onInvoke (webpack:///~/@angular/core/src/zone/ng_zone.js:273:0 <- src/test.ts:38516:37)
at ZoneDelegate.invoke (webpack:///~/zone.js/dist/zone.js:329:0 <- src/test.ts:142884:32)
at Zone.run (webpack:///~/zone.js/dist/zone.js:126:0 <- src/test.ts:142681:43)
Upvotes: 0
Views: 426
Reputation: 14221
It looks to me like you are getting the exception because the Url
is not specified. Try setting the url on the options like so:
let responseOpts = new ResponseOptions(opts);
mockBackend.connections.subscribe(
(connection: MockConnection) => {
responseOpts.url = connection.request.url;
connection.mockError(new MockError(responseOpts));
});
The issue you are seeing might be because the error is not being caught anywhere and so it is bubbling up to zone. Try adding an error catch to your subscribe to handle exceptions or squelch the error, (error) => {}
.
this.route.params
.take(1)
.pluck('userAccountToken')
.switchMap((userAccountToken: string) =>
this.userAccountService.activateAccount(userAccountToken)
.switchMapTo(this.signinService.signinByUserAccountToken(userAccountToken))
)
.subscribe(() => this.router.navigate(['/dashboard']), (error) => {});
Upvotes: 1