George Edwards
George Edwards

Reputation: 9229

angular tests passing before async call has completed?

I have the following angular (4) test which should be testing a service, however, it seems to be passing before the Observable returns and expect get's hit.

   it('should enter the assertion', inject(
        [ MockBackend, CellService ],
        ( backend: MockBackend, s: CellService ) => {
            const urls = [];

            backend.connections.subscribe((connection: MockConnection) => {
                const req = connection.request;
                urls.push(req.url);
                if (req.method === RequestMethod.Get && req.url === '/api/getTest') {
                    connection.mockRespond(new Response(new ResponseOptions('enter mocked content')));
                }
            });
            s.getCell('powders').subscribe(val => expect(true).toBeFalsy())
        })
    );

I tried adding async/await, but that didn't make a difference. How can I do this?

Update:

This code passes too ...

it('should enter the assertion', async(inject(
    [ MockBackend, CellService ],
    ( backend: MockBackend, s: CellService ) => {
        const urls = [];

        backend.connections.subscribe((connection: MockConnection) => {
            const req = connection.request;
            urls.push(req.url);
            if (req.method === RequestMethod.Get && req.url === '/api/getTest') {
                connection.mockRespond(new Response(new ResponseOptions('enter mocked content')));
            }
        });
        s.getCell('powders').subscribe(val => expect(true).toBeFalsy())
    })
));

Upvotes: 2

Views: 1731

Answers (3)

Addis
Addis

Reputation: 2530

You can use waitForAsync which is intended exactly for this and works both with observables and promises.

Wraps a test function in an asynchronous test zone. The test will automatically complete when all asynchronous calls within this zone are done. Can be used to wrap an inject call. (doc)

it('...', waitForAsync(inject([AClass], (object) => {
  object.doSomething.subscribe(() => {
    expect(...);
  })
})));

Upvotes: 0

Paul Samsotha
Paul Samsotha

Reputation: 209012

Wrap the test in Angular's async

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

                         ---==== vvvv ===----
it('should enter the assertion', async(inject(
    [ MockBackend, CellService ],
    ( backend: MockBackend, s: CellService ) => {
        ...
    })
));

This will wrap the test in a test zone, which will allow Angular to actually wait for all the asynchronous tasks to complete before finishing the test.

See also:

UPDATE

Try it with fakeAsycn/tick

import { fakeAsync } from '@angular/core/testing';

it('should enter the assertion', fakeAsync(inject(
    [ MockBackend, CellService ],
    ( backend: MockBackend, s: CellService ) => {
        ...

        let value;
        s.getCell('powders').subscribe(val => {
          value = val;
        })
        tick();
        expect(val).toBeTruthy();
    })
));

It should work with async, but using fakeAsync makes it a little easier to debug, since every thing is synchronous.

If it still doesn't work, then you may need to check the logic somewhere else. One of the checks I would make is here

req.method === RequestMethod.Get && req.url === '/api/getTest'

Are you sure both of these pass? If not, there will be no response.

Upvotes: 2

s-f
s-f

Reputation: 2141

I would prefer to use Jasmine done() callback here.

First, get rid off injectors in the spec and use TestBed.get(serviceToken) in beforeEach section.

Second, pass done as parameter of spec function

it('should use asynchronous', done => {
  s.method().subscribe(result => {
    expect(result).toBe(...);
    done();
  }
})

Upvotes: 0

Related Questions