Not all computations get settled when testing a complex effector application with fork and mocked handlers

40 views Asked by At

I'm struggling to get the below effector application tested:

// === module1.js ===
export const $storeA = createStore({testA: false});
export const $storeB = createStore({testB: false});

export const fetchDataFx = createEffect(() => someAsyncFnUsingAxios());

// === module2.js ===
$storeB.on(fetchDataFx.doneData, (_, {data}) => ({testB: data}));

// === module3.js ===
export const $storeC = combine($storeA, $storeB).map(([storeA, storeB]) => storeA && storeB);

// === module3.test.js ===
import {fetchDataFx, $storeA} from 'module1';
import {$storeC} from 'module3';

describe('some test', () => {
  test('should return true when fetched data is true', async () => {
    const scope = fork({
      handlers: [
        [fetchDataFx, () => Promise.resolve({data: true})],
      ],
      values: [
        [$storeA, {testA: true}],
        // not setting $storeB on purpose, as we want to test whether the fetched data is stored and calculated
      ],
    });

    await allSettled(fetchDataFx, {scope});

    expect(scope.getState($storeC)).toBe(true); // FAILS!
  });
});

The mocked handler gets called, but

$storeB.on(fetchDataFx.doneData, (_, {data}) => ({testB: data}));

does not get triggered!

Any ideas?

I've tried following the instructions from https://dev.to/effector/the-best-part-of-effector-4c27

I've also tried replacing the derived store with a regular one and use sample to make it update:

export const $storeC = createStore(false);

sample({
  source: [$storeA, $storeB],
  fn: ([storeA, storeB]) => storeA && storeB,
  target: $storeC
});

but it also didn't work.

1

There are 1 answers

0
Daniel Klimuntowski On BEST ANSWER

The problem turned out to be with imports. The test module must import any module that has some effector computations defined in order for effector to be aware of them. So obvious now ...

// === module3.test.js ===
import {fetchDataFx, $storeA} from 'module1';
import {$storeC} from 'module3';

import 'module2'; // <-- this solves the problem

describe('some test', () => {
  test('should return true when fetched data is true', async () => {
    const scope = fork({
      handlers: [
        [fetchDataFx, () => Promise.resolve({data: true})],
      ],
      values: [
        [$storeA, {testA: true}],
        // not setting $storeB on purpose, as we want to test whether the fetched data is stored and calculated
      ],
    });

    await allSettled(fetchDataFx, {scope});

    expect(scope.getState($storeC)).toBe(true); // SUCCEEDS!
  });
});