{"id":14888,"library":"redux-saga-test-engine","title":"Redux Saga Test Engine","description":"redux-saga-test-engine is a testing utility designed to simplify the process of testing Redux Saga generator functions. It provides a structured API for collecting specific Redux Saga effects (like `PUT` and `CALL`) and mapping yielded effects to predefined return values, eliminating the need for manual iteration over generator steps. The current stable version is 3.0.0. While no explicit release cadence is stated, major versions introduce breaking changes to the top-level API, with minor versions providing feature enhancements and bug fixes. It differentiates itself from alternatives like `redux-saga-test` and `redux-saga-test-plan` by offering a distinct approach to effect collection and environment stubbing, aiming for a more direct and less verbose testing experience, particularly when focusing on the effects a saga produces rather than its step-by-step execution.","status":"active","version":"3.0.0","language":"javascript","source_language":"en","source_url":"https://github.com/DNAinfo/redux-saga-test-engine","tags":["javascript","redux","redux-saga","test"],"install":[{"cmd":"npm install redux-saga-test-engine","lang":"bash","label":"npm"},{"cmd":"yarn add redux-saga-test-engine","lang":"bash","label":"yarn"},{"cmd":"pnpm add redux-saga-test-engine","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"ESM import is preferred in modern JavaScript/TypeScript projects. CommonJS `require` is also supported.","wrong":"const { createSagaTestEngine } = require('redux-saga-test-engine')","symbol":"createSagaTestEngine","correct":"import { createSagaTestEngine } from 'redux-saga-test-engine'"},{"note":"`collectPuts` is a convenience function, equivalent to `createSagaTestEngine(['PUT'])`. ESM import is preferred.","wrong":"const collectPuts = require('redux-saga-test-engine').collectPuts","symbol":"collectPuts","correct":"import { collectPuts } from 'redux-saga-test-engine'"},{"note":"Used for providing a sequence of yielded values for an effect. ESM import is preferred.","wrong":"const stub = require('redux-saga-test-engine').stub","symbol":"stub","correct":"import { stub } from 'redux-saga-test-engine'"},{"note":"Used for simulating errors thrown by an effect within the saga. ESM import is preferred.","wrong":"const throwError = require('redux-saga-test-engine').throwError","symbol":"throwError","correct":"import { throwError } from 'redux-saga-test-engine'"}],"quickstart":{"code":"import { createSagaTestEngine } from 'redux-saga-test-engine';\nimport { select, call, put } from 'redux-saga/effects';\n\n// Mock API and selectors for the example\nconst API = {\n  doWeLovePuppies: () => ({ answer: 'Of course we do!' })\n};\nconst getPuppy = () => ({ barks: true, cute: 'Definitely' });\nconst petPuppy = (puppy) => ({ type: 'PET_PUPPY', payload: puppy });\nconst hugPuppy = (puppy) => ({ type: 'HUG_PUPPY', payload: puppy });\n\n// The saga to be tested\nfunction* sagaToTest(action) {\n  const puppyState = yield select(getPuppy);\n  const apiResponse = yield call(API.doWeLovePuppies);\n\n  if (puppyState.cute && apiResponse.answer) {\n    yield put(petPuppy(puppyState));\n    yield put(hugPuppy(puppyState));\n  }\n}\n\n// Choose which effect types you want to collect from the saga.\nconst collectEffects = createSagaTestEngine(['PUT', 'CALL']);\n\n// Define environment mappings and initial action\nconst initialAction = { type: 'START_SAGA' };\nconst envMapping = [\n  [select(getPuppy), { barks: true, cute: 'Definitely' }],\n  [call(API.doWeLovePuppies), { answer: 'Of course we do!' }]\n];\n\nconst actualEffects = collectEffects(\n  sagaToTest,\n  envMapping,\n  initialAction\n);\n\nconsole.log(JSON.stringify(actualEffects, null, 2));\n/*\nExpected output (simplified):\n[\n  { \"@@redux-saga/IO\": true, \"call\": { \"args\": [], \"fn\": {} } }, // call(API.doWeLovePuppies)\n  { \"@@redux-saga/IO\": true, \"put\": { \"action\": { \"type\": \"PET_PUPPY\", \"payload\": { \"barks\": true, \"cute\": \"Definitely\" } } } },\n  { \"@@redux-saga/IO\": true, \"put\": { \"action\": { \"type\": \"HUG_PUPPY\", \"payload\": { \"barks\": true, \"cute\": \"Definitely\" } } } }\n]\n*/","lang":"javascript","description":"Demonstrates basic usage of `createSagaTestEngine` to test a Redux Saga, including how to collect specific effect types and provide mock responses for `select` and `call` effects, effectively isolating the saga logic."},"warnings":[{"fix":"Review the README for the current API (v3.0.0) and refactor test setup to use `createSagaTestEngine` or the `collectPuts` helper, adapting environment mappings to the new array-of-arrays format for yielded values.","message":"The top-level API underwent significant changes in version 2.0.0. Existing tests written for 1.x versions will require updates, particularly regarding how effects are collected and how the testing engine is initialized. The `createSagaTestEngine` function and explicit effect collection array (`['PUT', 'CALL']`) are part of the new API.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"Use `stub()` with a generator function or an object that correctly simulates the expected return value, including any methods. For example, `[call(apiCall), stub(function* () { yield { json: () => ({ data: 'mock' }) } })]`.","message":"When stubbing `call` effects that return objects with methods (e.g., an API response object that you expect to call `.json()` on), ensure your stub provides an object with the expected method. Otherwise, your saga will throw an error like 'response.json is not a function'.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Carefully review your saga and ensure every `select` and `call` (or any other effect you're not collecting) has an entry in the `envMapping` array, providing the exact value the saga should receive from that yielded effect.","message":"All non-`PUT` effects yielded by the saga (e.g., `select`, `call`) must have a corresponding mapping in the `envMapping` argument. If an effect is yielded that isn't mapped, `redux-saga-test-engine` will not know what value to return to the saga, potentially causing the saga to hang or the test to fail unexpectedly.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Ensure the stub for the `call` effect returns an object that accurately mimics the expected API response, including any methods like `json()` that the saga will invoke. Example: `[call(API.fetch), { json: () => ({}) }]` or using `stub()` with a generator that yields such an object.","cause":"A `call` effect was stubbed with a value that does not have the `.json()` method, but the saga attempted to call it.","error":"TypeError: response.json is not a function"},{"fix":"Inspect the saga under test and the `envMapping` to confirm that every `select` and `call` effect is explicitly mapped to a return value. Ensure the effect object passed as the key in the mapping exactly matches the effect yielded by the saga.","cause":"A `select` or `call` effect within the tested saga did not have a corresponding entry in the `envMapping`, preventing the test engine from progressing the saga.","error":"Test timeout: Async callback was not invoked within the timeout period."}],"ecosystem":"npm"}