You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A simple example: This module driver exports a function fetchDriversByState, and for test cases (testing other functions, that might internally call driver.fetchDriversByState), we don't want that function's implementation actually used.
// server/driver.ts"use server";exportconstfetchDriversByState=(async(state: string)=>{constresponse=awaitfetch(`...`);// ...});// ... the real module has many additional exported properties, irrelevant to this demonstration ...
Writing test cases of a different area of the application, I need to mock this driver module so the driver.fetchDriversByState is a mock object. This needs to be done in some unit tests but not all, of course; the mock should replace the real function, only within the unit tests that don't require the real driver.fetchDriverByState implementation.
Attempt to mock the module namespace: jest.mock("@/server/driver")
// tests/input-form.test.tsximport{describe,expect,jest,test}from"@jest/globals";import"whatwg-fetch";import*asdriverfrom"@/server/driver";jest.mock("@/server/driver");describe("JestJS mock of import path '@/server/driver'",(()=>{test("should mock the imported module `driver`",(async()=>{console.assert(jest.isMockFunction(driver));console.info("driver:",driver);console.assert(jest.isMockFunction(driver.fetchDriversByState));console.info("driver.fetchDriversByState:",driver.fetchDriversByState);driver.fetchDriversByState("LOREM");expect(driver.fetchDriversByState).toHaveBeenCalled();}),);}));// ... the real suite has many additional test cases, some of which use or do not use the 'driver' module ...
The test case fails unexpectedly, because the expect.toHaveBeenCalled method determines the driver.fetchDriversByState function is not a mock:
FAIL src/components/form/test/demonstrating-module-mock.test.ts
● JestJS mock of import path '@/server/driver' › should mock the imported module `driver`
expect(received).toHaveBeenCalled()
Matcher error: received value must be a mock or spy function
Received has type: function
Received has value: [Function fetchDriversByState]
Attempt to "mock a partial" of the module: jest.mock("@/server/driver", () => { /* ... */ })
Results in the same failure: The test case fails unexpectedly because the expect.toHaveBeenCalled method determines the driver.fetchDriversByState function is not a mock:
FAIL src/components/form/test/demonstrating-module-mock.test.ts
● JestJS mock of import path '@/server/driver' › should mock the imported module `driver`
expect(received).toHaveBeenCalled()
Matcher error: received value must be a mock or spy function
Received has type: function
Received has value: [Function fetchDriversByState]
Attempt to mock the specific function: jest.spyOn(driver, "fetchDriversByState")
Yet, when I attempt to use jest.spyOn to mock that function specifically, I get a different error:
// tests/input-form.test.tsximport{describe,expect,jest,test}from"@jest/globals";import"whatwg-fetch";import*asdriverfrom"@/server/driver";jest.spyOn(driver,"fetchDriversByState");describe("JestJS mock of imported function 'driver.fetchDriversByState'",(()=>{test("should mock the imported function `driver.fetchDriversByState`",(async()=>{console.assert(jest.isMockFunction(driver.fetchDriversByState));console.info("driver.fetchDriversByState:",driver.fetchDriversByState);driver.fetchDriversByState("LOREM");expect(driver.fetchDriversByState).toHaveBeenCalled();}),);}));// ... the real suite has many additional test cases, some of which use or do not use the 'driver' module ...
FAIL src/components/form/test/demonstrating-module-mock.test.ts
● Test suite failed to run
TypeError: Cannot redefine property: fetchDriversByState
at Function.defineProperty (<anonymous>)
12 |
13 |
> 14 | jest.spyOn(driver, "fetchDriversByState");
| ^
15 |
16 | describe("JestJS mock of imported function 'driver.fetchDriversByState'", (() => {
17 | test(
So: how can I mock this imported namespace using Jest mocks, and have that mock temporarily replace access at the driver.fetchDriversByState name?
Expected behavior
The driver namespace is valid, imported with import * as driver from "@/server/driver". So, this should be available to JestJS for mocking.
After jest.mock("@/server/driver"), the driver name should refer to a mock module, as described in the documentation.
After jest.spyOn(driver, "fetchDriversByState"), the driver.fetchDriversByState name should refer to a mock function, as described in the documentation.
Actual behavior
Attempt to mock the module namespace: jest.mock("@/server/driver")
FAIL src/components/form/test/demonstrating-module-mock.test.ts
● JestJS mock of import path '@/server/driver' › should mock the imported module `driver`
expect(received).toHaveBeenCalled()
Matcher error: received value must be a mock or spy function
Received has type: function
Received has value: [Function fetchDriversByState]
Attempt to "mock a partial" of the module: jest.mock("@/server/driver", () => { /* ... */ })
FAIL src/components/form/test/demonstrating-module-mock.test.ts
● JestJS mock of import path '@/server/driver' › should mock the imported module `driver`
expect(received).toHaveBeenCalled()
Matcher error: received value must be a mock or spy function
Received has type: function
Received has value: [Function fetchDriversByState]
Attempt to mock the specific function: jest.spyOn(driver, "fetchDriversByState")
FAIL src/components/form/test/demonstrating-module-mock.test.ts
● Test suite failed to run
TypeError: Cannot redefine property: fetchDriversByState
at Function.defineProperty (<anonymous>)
12 |
13 |
> 14 | jest.spyOn(driver, "fetchDriversByState");
| ^
15 |
16 | describe("JestJS mock of imported function 'driver.fetchDriversByState'", (() => {
17 | test(
Additional context
The module to be mocked is a NextJS server module. I don't know whether this makes a difference. If it does, how do we use this "mock a specific module for a specific test case" technique with NextJS?
The module to be mocked does not export a default, by design; it is intended that the module be imported as a namespace (import * as driver from "@/server/driver"). The unit test imports it that way deliberately. This should not make a difference; driver is still a module, the driver.fetchDriversByState is still a function to be mocked. How do we use the mock feature to achieve this?
Version
29.7.0
Steps to reproduce
When using the
jest.mock
method to mock a module namespace, the module is evidently not mocked.When using the
jest.spyOn
method to mock a function in the imported module, JestJS crashes the test case with a "TypeError: Cannot redefine property".A simple example: This module
driver
exports a functionfetchDriversByState
, and for test cases (testing other functions, that might internally calldriver.fetchDriversByState
), we don't want that function's implementation actually used.Writing test cases of a different area of the application, I need to mock this
driver
module so thedriver.fetchDriversByState
is a mock object. This needs to be done in some unit tests but not all, of course; the mock should replace the real function, only within the unit tests that don't require the realdriver.fetchDriverByState
implementation.Attempt to mock the module namespace:
jest.mock("@/server/driver")
The test case fails unexpectedly, because the
expect.toHaveBeenCalled
method determines thedriver.fetchDriversByState
function is not a mock:Attempt to "mock a partial" of the module:
jest.mock("@/server/driver", () => { /* ... */ })
Using the "mocking partials" technique:
Results in the same failure: The test case fails unexpectedly because the
expect.toHaveBeenCalled
method determines thedriver.fetchDriversByState
function is not a mock:Attempt to mock the specific function:
jest.spyOn(driver, "fetchDriversByState")
Yet, when I attempt to use
jest.spyOn
to mock that function specifically, I get a different error:So: how can I mock this imported namespace using Jest mocks, and have that mock temporarily replace access at the
driver.fetchDriversByState
name?Expected behavior
The
driver
namespace is valid, imported withimport * as driver from "@/server/driver"
. So, this should be available to JestJS for mocking.After
jest.mock("@/server/driver")
, thedriver
name should refer to a mock module, as described in the documentation.After
jest.spyOn(driver, "fetchDriversByState")
, thedriver.fetchDriversByState
name should refer to a mock function, as described in the documentation.Actual behavior
Attempt to mock the module namespace:
jest.mock("@/server/driver")
Attempt to "mock a partial" of the module:
jest.mock("@/server/driver", () => { /* ... */ })
Attempt to mock the specific function:
jest.spyOn(driver, "fetchDriversByState")
Additional context
The module to be mocked is a NextJS server module. I don't know whether this makes a difference. If it does, how do we use this "mock a specific module for a specific test case" technique with NextJS?
The module to be mocked does not export a default, by design; it is intended that the module be imported as a namespace (
import * as driver from "@/server/driver"
). The unit test imports it that way deliberately. This should not make a difference;driver
is still a module, thedriver.fetchDriversByState
is still a function to be mocked. How do we use the mock feature to achieve this?Environment
The text was updated successfully, but these errors were encountered: