Pragmatic Testing in AdonisJS with Japa #2.3

Testing Async Code & Exceptions

In This Lesson

We'll cover testing asynchronous code in AdonisJS using Japa. Learn to assert successful promises with both async/await and promise chaining. We'll then discuss testing rejected promises and what to watch for to prevent false positives.

Created by
@tomgobich
Published

Notes Used to Craft

Next lets take a look at some assertions when it comes to asynchronous code and promises.

node ace make:test async --suite=unit
Copied!

For context, I have within the app/services folder a TestService that contains some utility methods to make stepping through some of these test scenarios easier. In these tests, we'll be using this getUser asynchronous method.

To start, let's take a look at a successful call, so we'll want to pass an id of 1, as the method expects.

test("successfully get a user with an id of 1 (async/await)", async ({
  assert,
}) => {
  const testService = new TestService();
  const user = await testService.getUser(1);

  assert.equal(user.id, 1);
  assert.equal(user.name, "John Doe");
});
Copied!

When we expect all to go according to plan, we can treat this as a traditional promise and just await it.

If, however, you have a promise that you need to use chaining with, that is supported as well. The test callback provides a done method that we can use to signal the promise's completion. We also need to inform the test to actually wait for done to be called.

test("successfully get a user with an id of 1 (chaining)", async ({
  assert,
}, done) => {
  const testService = new TestService();
  testService.getUser(1).then((user) => {
    assert.equal(user.id, 1);
    assert.equal(user.name, "John Doe");
    done();
  });
}).waitForDone();
Copied!

Great, now what about when an exception is raised? We actually have a few options for testing these. First, let's take a look at this with a try/catch.

test("throw an exception when user is not found (try/catch)", async ({
  assert,
}) => {
  const testService = new TestService();

  try {
    await testService.getUser(2);
  } catch (error) {
    assert.equal(error.status, 404);
    assert.equal(error.code, "E_USER_NOT_FOUND");
    assert.equal(error.message, "User not found");
  }
});
Copied!

The downside to using a try/catch, however, is that should getUser never throw an exception, we'll get a false positive result because our test will still succeed. There aren't any assertions that will run if an exception isn't thrown.

To fix this, we can assert that the method should return a rejected promise.

test("throw an exception when user is not found (rejects)", async ({
  assert,
}) => {
  const testService = new TestService();

  // note this also accepts regex for the message
  await assert.rejects(async () => testService.getUser(2), "User not found");
});
Copied!

Finally, we can put it all together to support a failed promise chain as well.

test("throw an exception when user is not found (chaining)", async ({
  assert,
}, done) => {
  const testService = new TestService();
  testService.getUser(2).catch((error) => {
    assert.equal(error.status, 404);
    assert.equal(error.code, "E_USER_NOT_FOUND");
    assert.equal(error.message, "User not found");
    done();
  });
}).waitForDone();
Copied!

Join the Discussion 0 comments

Create a free account to join in on the discussion
robot comment bubble

Be the first to comment!