7

I have a couple of tests I'd like to run on the .then and .catch blocks of one of my dependencies.

import test from 'ava';
import sinon from 'sinon';

// Fake dependency code - this would be imported
const myDependency = {
    someMethod: () => {}
};

// Fake production code - this would be imported
function someCode() {
    return myDependency.someMethod()
        .then((response) => {
            return response;
        })
        .catch((error) => {
            throw error;
        });
}

// Test code

let sandbox;

test.beforeEach(() => {
    sandbox = sinon.sandbox.create();
});

test.afterEach.always(() => {
    sandbox.restore();
});

test('First async test', async (t) => {
    const fakeResponse = {};

    sandbox.stub(myDependency, 'someMethod')
        .returns(Promise.resolve(fakeResponse));

    const response = await someCode();

    t.is(response, fakeResponse);
});

test('Second async test', async (t) => {
    const fakeError = 'my fake error';

    sandbox.stub(myDependency, 'someMethod')
        .returns(Promise.reject(fakeError));

    const returnedError = await t.throws(someCode());

    t.is(returnedError, fakeError);
});

If you run either test alone, the test passes. But if you run these together, the setup for the test A runs, and then before it completes, the setup for test B runs and you get this error:

Second async test
   failed with "Attempted to wrap someMethod which is already wrapped"

Maybe I'm not understanding how I should be setting up my tests. Is there way to force Test A to complete before Test B starts running?

sak_to
  • 399
  • 4
  • 13

2 Answers2

15

AVA tests are run concurrently, which messes up your Sinon stubbing.

Instead, declare your tests to be run serially and it should work:

test.serial('First async test', ...);
test.serial('Second async test', ...);
robertklep
  • 198,204
  • 35
  • 394
  • 381
  • 14
    To provide more background on this, yes, by default tests are run concurrently. This means the `beforeEach` hook for all tests are run concurrently, too. What's happening is that each invocation of the `beforeEach` hook replaces the `sandbox` variable, so both tests actually use the same sandbox. You can fix that by assigning to `t.context` in the `beforeEach`, which will be available in the test as well. However both tests are still modifying the same dependency, at the same time, so the second test will still fail to stub the method. Hence you need `test.serial`. – Mark Wubben Oct 06 '16 at 17:11
  • Thanks for the insight, @MarkWubben :) – robertklep Oct 06 '16 at 19:24
0

Running serially will slow down the running of your tests. You could, of course, create separate variables sandbox1 and sandbox2 to prevent the first test from using the sandbox set up in the 2nd test. But what I do to ensure that I'm not making this mistake is to run each test in an IIFE (immediately invoked function expression). Any variable in one IIFE is distinct from any variable in another IIFE, even if they have the same names:

(function () {
   ...one test
   });
(function () {
   ...anothertest
   });
John Deighan
  • 4,329
  • 4
  • 18
  • 19