I was experimenting with Jest and the fetch API and discovered a strange behaviour.
Consider the route:
debug.get('/echo', (req, res) => {
const { value } = req.query;
console.log(`[ ECHO ] ${value}`);
res.json({ value });
});
And the test:
it('echoes the given content', async () => {
const res = await fetch(`${URL}/debug/echo?value=hello`);
const data = await res.json();
expect(data).toStrictEqual({ value: 'hello' });
});
This produces the following output:
FAIL tests/debug.test.ts
● /debug/echo › echoes the given content
expect(received).toStrictEqual(expected) // deep equality
Expected: {"value": "hello"}
Received: serializes to the same string
8 | const data = await res.json();
9 | console.log(data);
> 10 | expect(data).toStrictEqual({ value: 'hello' });
| ^
11 | });
12 | });
13 |
at Object.toStrictEqual (tests/debug.test.ts:10:18)
However, when I replace the toStrictEqual
with a toEqual
, the test passes as expected.
Why is this the case?
This appears to be due to some strangeness in Jest's environment, which is discussed in this blog post, and in this issue.
Essentially, due to Jest's mangling of the environment, some instanceof
operations fail, and seemingly this is leading to the results of fetch
's Response json
function failing to match when using a .toStrictEqual
.
To work around this, a working solution is to clone the object. This can be done using a strategy similar to this code in the jewire
library.
For my test cases, I was able to get away with an const data = Object.assign({}, await res.json());
, but this may not be 100% reliable for nested objects.
Note that structuredClone
does not work, since it seems to preserve the isinstance incorrectness.