mjackson / expect

Write better assertions
MIT License
2.29k stars 117 forks source link

How to test promises? #106

Closed diegohaz closed 8 years ago

diegohaz commented 8 years ago

I was looking for expect(Promise.resolve()).toBeFulfilled() or expect(Promise.reject()).toBeRejected(). Is there something like this?

ljharb commented 8 years ago

There's no way to synchronously check the state of a Promise, so you'd have to do it asynchronously - and depending on your test framework, that can't really be done transparently in this framework.

In other words, you want promise.then(succeed).catch(fail) where success and fail depend on your test framework.

batjko commented 8 years ago

@ljharb If I understand you correctly, this should work (using Mocha's async handling with the done() callback):

describe('getThing()', () => {
    it('retrieves a thing by an id', (done) => {

      const spy = expect.spyOn(thingService, 'query')
        .andReturn(Promise.resolve({ name: 'My Thang', id: 'xxx' }));

      getThings('xxx')
        .then(thing => {
          expect(spy.calls.length).toEqual(1);
          expect(spy.calls[0].arguments).toEqual('xxx');
          expect(thing).toEqual({ name: 'My Thang', id: 'xxx' });
          done();
        });
    });
});

getThings() is a function that runs thingService.query() behind the scenes and returns a promise with the result.

However, the .then call of getThings() is never being entered. Mocha keeps throwing me the standard timeout message that the done() doesn't get called at all.

Now, I tried putting the spy in a beforeEach first, but the result is the same. I don't know if that's an issue with expect's spyOn, which just doesn't handle promises or something?

ljharb commented 8 years ago

Instead of the done callback, simply return the promise. mocha will wait on it, and will fail the test if the promise rejects.

batjko commented 8 years ago

Ah, thanks. Just read about this in Mocha's documentation and tried it, but I'm still getting the exact same error:

    it('retrieves a thing by id', () => {
      return getThing('xxx')
        .then(thing => {
          expect(spy.calls.length).toEqual(1);
          expect(spy.calls[0].arguments).toEqual('xxx');
          expect(thing).toEqual({ name: 'My Thang', id: 'xxx' });
        });
    });

So the error still seems to expect a done() callback? Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.

Edit:

I just found out that the issue seems to be my spy not working, i.e. the test still hits the thingService, which I tried to mock with the spy above.

Is this not right?

const spy = expect.spyOn(thingService, 'query')
   .andReturn(Promise.resolve({ name: 'My Thang', id: 'xxx' }));
batjko commented 8 years ago

Found the problem, and it was to do with the way the dependent service was imported in the module being tested (see #169).