Testdouble usage of td.when not getting invoked inside td.object

302 Views Asked by At

Probably my misunderstanding of Testdouble, but I've created this example to illustrate the issue I'm having:

const test = require("ava");
const td = require("testdouble");

const reducer = async (state, event) => {
  if (event.id === "123") {
    const existing = await state.foo("", event.id);
    console.log("existing:", existing);
  }
};

test("foo", async (t) => {
  const foo = td.func(".foo");

  const state = td.object({
    foo
  });

  td.when(foo(td.matchers.anything(), td.matchers.anything())).thenResolve({
    id: "123"
  });

  await reducer(state, {
    id: "123",
    nickname: "foo"
  });
});

This logs: existing: undefined

Whereas I believe it should log: existing: { id: "123" } as stated by the td.when()

What am I missing?

1

There are 1 best solutions below

0
On

I think your issue is that td.object doesn't work the way you think it does:

td.object(realObject) - returns a deep imitation of the passed object, where each function is replaced with a test double function named for the property path (e.g. If realObject.invoices.send() was a function, the returned object would have property invoices.send set to a test double named '.invoices.send')

Source: https://github.com/testdouble/testdouble.js#tdobject

So when you do…

const foo = td.func();
const obj = td.object({foo});

… you're actually mocking foo twice.

Here's a demo:

const td = require('testdouble');

const num2str = td.func('num->string');

td.when(num2str(42)).thenReturn('forty two');
td.when(num2str(43)).thenReturn('forty three');
td.when(num2str(44)).thenReturn('forty four');

const x = {num2str};
const y = td.object({num2str});

num2str(42);
x.num2str(43);
y.num2str(44);

Then we can inspect x.num2str and we can see that it is the same test double as num2str:

td.explain(x.num2str).description;
/*
This test double `num->string` has 3 stubbings and 2 invocations.

Stubbings:
  - when called with `(42)`, then return `"forty two"`.
  - when called with `(43)`, then return `"forty three"`.
  - when called with `(44)`, then return `"forty four"`.

Invocations:
  - called with `(42)`.
  - called with `(43)`.
*/

However y.num2str is a completely different test double:

td.explain(y.num2str).description;
/*
This test double `.num2str` has 0 stubbings and 1 invocations.

Invocations:
  - called with `(44)`.
*/

I think what you're looking for is the "property replacement" behaviour of td.replace:

const z = {
  str2num: str => parseInt(str),
  num2str: num => {
    throw new Error('42!');
  }
};

td.replace(z, 'num2str');

td.when(z.num2str(42)).thenReturn('FORTY TWO!!');

The z.str2num function hasn't been mocked:

z.str2num("42");
//=> 42

However z.num2str is a fully-fledged test double:

z.num2str(42);
//=> 'FORTY TWO!!'

td.explain(z.num2str).description;
/*
This test double `num2str` has 1 stubbings and 1 invocations.

Stubbings:
  - when called with `(42)`, then return `"FORTY TWO!!"`.

Invocations:
  - called with `(42)`.
*/