How to test redux-saga delay
Asked Answered
M

3

14

Problem

In redux-saga, I am using yield delay(1000);. During my unit test, I do expect(generator.next().value).toEqual(delay(1000));.

I expect the test to pass.

This is my sagas.js:

import { delay } from 'redux-saga';

export function* incrementAsync() {
  yield delay(1000);
}

This is my sagas.test.js

import { delay } from 'redux-saga';
import { incrementAsync } from '../sagas';

describe('incrementAsync Saga test', () => {
  it('should incrementAsync', () => {
    const generator = incrementAsync();
    expect(generator.next().value).toEqual(delay(1000));
  });
});

● incrementAsync Saga test › should incrementAsync

expect(received).toEqual(expected)

Expected value to equal:
  {"@@redux-saga/CANCEL_PROMISE": [Function anonymous]}
Received:
  {"@@redux-saga/CANCEL_PROMISE": [Function anonymous]}

Difference:

Compared values have no visual difference.

question

How can I test redux-saga delay ?

Mckinney answered 13/3, 2018 at 18:11 Comment(0)
P
10

If you check delay saga effect code you can see that it is a bound function:

export const delay = call.bind(null, delayUtil)

so if you import delay in two different modules it will be two different functions that have no visual difference.

You can check this in the codesandbox example (see test tab):

Edit redux-saga delay test

const testFunction = () => {};

describe("example bound functions equality test", () => {
  it("Two bound functions are not equal", () => {
    expect(testFunction.bind(this))
      .not.toEqual(testFunction.bind(this));
  });
});

Result is: enter image description here

To test your saga you should mock your delay effect (if you are using Jest);

import { delay } from "redux-saga";
import { incrementAsync } from "../sagas";

jest.mock("redux-saga");

describe("incrementAsync Saga test", () => {
  it("should incrementAsync", () => {
    const generator = incrementAsync();
    expect(generator.next().value).toEqual(delay(1000));
  });
});
Peppi answered 13/3, 2018 at 21:12 Comment(0)
A
12

A good way to test Redux Saga calls is by using the call effect. In this case, you can slightly refactor your saga as follows:

import { delay } from 'redux-saga';
import { call } from 'redux-saga/effects';

export function* incrementAsync() {
  yield call(delay, 1000);
}

You would then test this like so:

import { delay } from 'redux-saga';
import { call } from 'redux-saga/effects';

describe('incrementAsync', () => {
  it('should incrementAsync()', () => {
    const generator = incrementAsync();

    expect(generator.next().value).toEqual(call(delay, 1000));
  });
});

This works because the result of the yield to call is a simple object, describing a call to the delay function. No need for any mocks :)

There is also of course the great redux-saga-test-plan helper library. Using that, your test would become the following:

import { testSaga } from 'redux-saga-test-plan';
import { delay } from 'redux-saga';
import { call } from 'redux-saga/effects';

describe('incrementAsync', () => {
  it('should incrementAsync()', () => {
    testSaga(incrementAsync)
      .next()
      .call(delay, 1000)
      .next()
      .isDone();
  });
});
Apocalypse answered 12/10, 2018 at 22:18 Comment(3)
If I do that in expectSaga(...) call stack, it gives me the following error: instead of writing 'yield call(delay, 600)' where delay is an effect from 'redux-saga/effects' you should write 'yield delay(600)'Laird
Interesting, is that using the latest version? It might have changed since I gave this answer.Apocalypse
Yes @FelaMaslen that's for the last version, I just faced this problem on project.Eggshaped
P
10

If you check delay saga effect code you can see that it is a bound function:

export const delay = call.bind(null, delayUtil)

so if you import delay in two different modules it will be two different functions that have no visual difference.

You can check this in the codesandbox example (see test tab):

Edit redux-saga delay test

const testFunction = () => {};

describe("example bound functions equality test", () => {
  it("Two bound functions are not equal", () => {
    expect(testFunction.bind(this))
      .not.toEqual(testFunction.bind(this));
  });
});

Result is: enter image description here

To test your saga you should mock your delay effect (if you are using Jest);

import { delay } from "redux-saga";
import { incrementAsync } from "../sagas";

jest.mock("redux-saga");

describe("incrementAsync Saga test", () => {
  it("should incrementAsync", () => {
    const generator = incrementAsync();
    expect(generator.next().value).toEqual(delay(1000));
  });
});
Peppi answered 13/3, 2018 at 21:12 Comment(0)
C
0

If anyone hits the below issue when using the redux-saga-test-plan package.

instead of writing 'yield call(delay, 600)' where delay is an effect from 'redux-saga/effects' you should write 'yield delay(600)'

You should do this:

saga.ts:

import { delay } from 'redux-saga/effects';

export function* incrementAsync() {
  yield delay(1000);
}

saga.test.ts:

import { testSaga } from "redux-saga-test-plan"
import { incrementAsync } from "./saga"

describe('incrementAsync', () => {
  test('should pass', () => {
    testSaga(incrementAsync).next().delay(1000).next().isDone();
  })
})

Test result:

 PASS   redux-saga-examples  packages/redux-saga-examples/src/stackoverflow/49263089/saga.test.ts
  incrementAsync
    ✓ should pass (3 ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |                   
 saga.ts  |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.208 s

package versions:

"redux-saga-test-plan": "^4.0.1",
"redux-saga": "^1.1.3",
Cantharides answered 18/10, 2022 at 6:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.