Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to handle async callbacks #380

Closed
jsardev opened this issue Aug 23, 2018 · 3 comments
Closed

how to handle async callbacks #380

jsardev opened this issue Aug 23, 2018 · 3 comments

Comments

@jsardev
Copy link

jsardev commented Aug 23, 2018

Description

For some reason td.verify does not work well with subjects that use async/await callbacks.

Take a look at this example:

const t = require('tap');
const td = require('testdouble');

const printBalance = (id, fetchBalance, getMessage, print) => {
  fetchBalance(id, async (error, balance) => {
    const message = await getMessage(balance);
    print(message);
  });
};

t.test('should print 1337', async () => {
  const fetchBalance = td.function('.fetchBalance');
  const getMessage = td.function('.getMessage');
  const print = td.function('.print');
  td.when(fetchBalance(42)).thenCallback(null, 1337);
  td.when(getMessage(1337)).thenResolve('Your balance is 1337');

  printBalance(42, fetchBalance, getMessage, print);

  td.verify(print('Your balance is 1337'));
});

This test doesn't work throwing there were no invocations of the test double.. This totally doesn't make sense to me as the callback thanks to async/await is being processed synchronously which should make the print function invoke immediately.

Any ideas what's wrong in here?

Environment

  • node: 9.11.2
  • npm: 5.6.0
  • testdouble: 3.8.1
@jsardev
Copy link
Author

jsardev commented Aug 23, 2018

I came to a solution. But is this really the only way to handle this kind of situation?

const t = require('tap');
const td = require('testdouble');

const printBalance = (id, fetchBalance, getMessage, print) => {
  fetchBalance(id, async (error, balance) => {
    const message = await getMessage(balance);
    print(message);
  });
};

t.test('should print 1337', t => {
  const fetchBalance = td.function('.fetchBalance');
  const getMessage = td.function('.getMessage');
  const print = td.function('.print');
  td.when(fetchBalance(42)).thenCallback(null, 1337);
  td.when(getMessage(1337)).thenResolve('Your balance is 1337');

  td.when(print('Your balance is 1337')).thenDo(() => {
    t.end();
  });

  printBalance(42, fetchBalance, getMessage, print);
});

@searls
Copy link
Member

searls commented Aug 23, 2018

I read your first example, but I'm struggling to understand how it would work. When you resolve a promise, even if no network activity occurs, it will defer a stack and so won't trigger synchronously.

That means, if I'm reading you correctly, this code:

const printBalance = (id, fetchBalance, getMessage, print) => {  // sync - stack 1
  fetchBalance(id, async (error, balance) => { // sync - stack 1
    const message = await getMessage(balance); // async! - message will be assigned on stack 2
    print(message); // stack 2
  });
};

As a result, in your first text example:

  td.when(getMessage(1337)).thenResolve('Your balance is 1337'); // stack 1

  printBalance(42, fetchBalance, getMessage, print); // stack 1 but schedules resolution of `getMessage` to stack 2

  td.verify(print('Your balance is 1337')); // stack 1 -- message will not have been printed yet.

Therefore, if you make printBalance an async function and then await its result, as done here, your test will pass:

const t = require('tap');
const td = require('testdouble');

const printBalance = async (id, fetchBalance, getMessage, print) => {
  fetchBalance(id, async (error, balance) => {
    const message = await getMessage(balance);
    print(message);
  });
};

t.test('should print 1337', async () => {
  const fetchBalance = td.function('.fetchBalance');
  const getMessage = td.function('.getMessage');
  const print = td.function('.print');
  td.when(fetchBalance(42)).thenCallback(null, 1337);
  td.when(getMessage(1337)).thenResolve('Your balance is 1337');

  await printBalance(42, fetchBalance, getMessage, print);

  td.verify(print('Your balance is 1337'));
});

@searls searls closed this as completed Aug 23, 2018
@jsardev
Copy link
Author

jsardev commented Aug 23, 2018

@searls Damn, you're right 😄 I guess I've just got lost in the async world and didn't see the thing here. Thanks and sorry for the problem! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants