1

Is there a way to test http timeout behaviour of a service?

I'm using MockBackend even when setting job's timeout to 0 no logging of 'TIMEOUT' present.

export class MyHttpService {
private sendGetRequest (job: HttpJob): Observable<any> {
    return this.http.get(job.getUrl(), this.options)
      .share()
      .timeout(job.getTimeout(), () => this.requestTimeout(job))
      .catch((err) => this.errorHandler(err, job));
  };

  private requestTimeout(job: HttpJob): Error {
    job.errorCode = ErrorCode.TIMEOUT;
    console.log('TIMEOUT');
    return new Error('timeout');
  }
...

test (nothing is logged)

 it('should resolve to TIMEOUT', () => {
      job.timeout = 0;
      let response: any;
      service.sendRequest(job).subscribe(
        (res: Response) => {
          response = res.json();
          console.log('OK', res);
        },
        err => {
          response = err;
          console.log('ER', err);
        }
      );
      expect(job.errorCode).toEqual(ErrorCode.TIMEOUT);
    });

Thx!

update: minimal example, if timeout is uncommented, it will fail

Petr Marek
  • 1,381
  • 1
  • 15
  • 31

1 Answers1

1

It's because the resolution of the subscribe method is asynchronous. You are trying to test synchronously.

it('..', () => {
  doSomthing().subscribe(() => {

  })
  expect(something)
})

Here, he expectation happens synchronously before any asynchronous tasks triggered by the subscription. And the test completes synchronously even before the asynchronous tasks are complete (that's why you never see the console.log)

What you need to do is either use async and do the expectation in the subscription callback

import { async } from '@angular/core/testing'

it('..', async(() => {
  doSomthing().subscribe(() => {
    expect(something)
  })
}))

Or use fakeAsync and force a fake synchronous resolution by calling tick

import { fakeAsync, tick } from '@angular/core/testing'

it('..', fakeAsync(() => {
  doSomthing().subscribe(() => {
    // this is called when you tick
  })
  tick();
  expect(something);
}))
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • you are right wrapping test in async(..) got me next, however now if `doSomthing()` contains `timeout(30000, new Error('....'))` in observable chain I receive this error: `Cannot use setInterval from within an async zone test`. How could I test http timeout? – Petr Marek Nov 17 '16 at 09:56
  • That's right, `setInterval` can't be called from `async`. Maybe `fakeAsync` also. And `timeout` calls `setInterval` internally. Not sure what you can do. Let me think about it. Im pretty sure it will fail also, but try to use `fakeAsync` – Paul Samsotha Nov 17 '16 at 09:58
  • OK I think I got the solution. Instead of either of those, just use the jasmine native `done`. `it('..', ((done) => {`, and call `done` inside the callback, after you make the expectation. I imagine that should work – Paul Samsotha Nov 17 '16 at 10:02
  • I tried that with `done()` but now i get this error `function (){__cov_eKH4RcGaeeuwa1xb1U0Ipg.f['9']++;__cov_eKH4RcGaeeuwa1xb1U0Ipg.s['33']++;return _this.requestTimeout(job);}` my `requestTimeout ` method is not called instead metioned err is catched in my `errorHandler` method – Petr Marek Nov 17 '16 at 10:11
  • Not sure. I would need a complete test to check it out – Paul Samsotha Nov 17 '16 at 10:21
  • test with the service: http://pastebin.com/AdTmMdHx, please take a look.. – Petr Marek Nov 17 '16 at 10:38
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/128334/discussion-between-petr-marek-and-peeskillet). – Petr Marek Nov 17 '16 at 10:39
  • minimal example...pastebin.com/ZXxSCrD7 if timeout is uncommented, it will fail.. – Petr Marek Nov 17 '16 at 13:33