next.js: Jest cannot test "use server" file

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
      Platform: linux
      Arch: x64
      Version: #1 SMP Fri Jan 27 02:56:13 UTC 2023
    Binaries:
      Node: 18.16.0
      npm: 9.5.1
      Yarn: 1.22.19
      pnpm: 8.5.1
    Relevant Packages:
      next: 13.4.12-canary.0
      eslint-config-next: N/A
      react: 18.2.0
      react-dom: 18.2.0
      typescript: 4.9.5
    Next.js Config:
      output: N/A


warn  - Latest canary version not detected, detected: "13.4.12-canary.0", newest: "13.4.12".
        Please try the latest canary version (`npm install next@canary`) to confirm the issue still exists before creating a new issue.

Which area(s) of Next.js are affected? (leave empty if unsure)

App Router, Jest (next/jest)

Link to the code that reproduces this issue or a replay of the bug

https://github.com/mkizka/nextjs-serveractions-jest

To Reproduce

git clone https://github.com/mkizka/nextjs-serveractions-jest
cd nextjs-serveractions-jest
yarn
yarn test

Describe the Bug

I got this message even though serverActions is true. This test passes if I remove “use server”.

$ cat next.config.js
/** @type {import("next").NextConfig} */
module.exports = {
  reactStrictMode: true,
  experimental: {
    serverActions: true,
  },
};

$ yarn test
yarn run v1.22.19
$ jest
- warn You have enabled experimental feature (serverActions) in next.config.js.
- warn Experimental features are not covered by semver, and may cause unexpected or broken application behavior. Use at your own risk.

 FAIL  app/action.test.ts
  ● Test suite failed to run


      × To use Server Actions, please enable the feature flag in your Next.js config. Read more: https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions#convention
       ╭─[/home/ubuntu/nextjs-serveractions-jest/app/action.ts:1:1]
     1 │ "use server";
       · ────────────
     2 │
     3 │ export const action = () => {
     4 │   console.log("action called");
       ╰────

      3 | jest.spyOn(console, "log");
      4 |
    > 5 | test("action", () => {
        |                 ^
      6 |   action();
      7 |   expect(console.log).toHaveBeenCalledWith("action called");
      8 | });

      at Object.transformSync (node_modules/next/src/build/swc/index.ts:1000:25)
      at Object.<anonymous> (app/action.test.ts:5:17)

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        0.15 s
Ran all test suites.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

Expected Behavior

The test succeeds.

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

NEXT-1473

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 5
  • Comments: 18 (2 by maintainers)

Commits related to this issue

Most upvoted comments

Hi can you upragde to v13.5.4-canary.6 , we land a change in that version to disable the SWC transform and erroring, so you should be able to test the components with basic UI testing tools

Something that works for me is to mock the module that uses server (i.e. that has the use server directive). For example, the component that I want to test (say ComponentA.tsx) renders a form. It imports a sever action from a file (say the path is @/server/actions.ts). This is the file that uses use server directive.

Here’s how I test the ComponentA.tsx:

// ComponentA.spec.tsx

import { postQuestionToServer } from '@/server/actions'

jest.mock('@/server/actions.ts', () => ({
  __esModule: true,
  postQuestionToServer: jest.fn(), // The server action that is called when the form is submitted
}));

describe('ComponentA', () => {
     beforeEach(() => {
             (postQuestionToServer as jest.Mock).mockImplementation((data: FormData) => {
                 return Promise.resolve([]); // or whatever the success value should be
            });
     });
});

Since I don’t really want to send a request to the server, this approach works well for me for unit testing the behavior of the client side component ComponentA.

Edit: I just realized that the above solution may not help if you want to test that the server action was called, however, you will be able to test other parts of the component.

test('does not work', () => {
    const spy = jest.spyOn(formActions, 'postQuestionToServer');

    const formEl = screen.getByTestId('my-form');
    fireEvent.submit(formEl);

    expect(spy).toHaveBeenCalled(); // This will NOT work
})

I got the same error, I tried different configurations on next.config.js but nothing seems to work I have two files

  • app/page.tsx which has ‘use client’ directive on the top of the file
  • actions/submit.ts which has ‘use server’ directive on the top of the file

app/page.tsx import a server action from actions/submit.ts and uses it as formAction in a form component.

I’m not trying to test the action itself but only if some components (no action dependent) are loaded in the html

Mocking a server action defined in a “use server” file seems to work perfectly, as long as you specify the entire mock (automock fails). Granted it’s not helpful for testing the impact of the action itself.

import { screen } from '@testing-library/react'
import { TodoList } from './TodoList';
import { addTodo } from './actions/addTodo';

jest.mock('./actions/addTodo', () => ({
  addTodo: jest.fn(),
}));

describe('TodoList', () => {
  it('calls action on submit', async () => {
    render(<TodoList />)
    fireEvent.input(screen.getByRole('textbox', { name: 'Message' }), { target: { value: 'new todo' }})
    fireEvent.click(screen.getByRole('button'))

    // wait for todos refreshed from server
    await screen.findByText('new todo')
    
    expect(addTodo).toHaveBeenCalledWith({ message: 'new todo'  })
  })
})

I am experiencing the same issue too. Cant test my form component which calls a server action on submit

@nicksan222 Thank you! But I got the same result. mkizka/nextjs-serveractions-jest#1

$ cat jest.config.mjs 
import nextJest from "next/jest.js";

const createJestConfig = nextJest({
  // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
  dir: "./",
});

// Add any custom config to be passed to Jest
/** @type {import('jest').Config} */
const config = {
  // Add more setup options before each test is run
  // setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  testEnvironment: "node",
};

// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
export default createJestConfig(config);

$ yarn test
yarn run v1.22.19
$ jest
- warn You have enabled experimental feature (serverActions) in next.config.js.
- warn Experimental features are not covered by semver, and may cause unexpected or broken application behavior. Use at your own risk.

 FAIL  app/action.test.ts
  ● Test suite failed to run


      × To use Server Actions, please enable the feature flag in your Next.js config. Read more: https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions#convention
       ╭─[/home/ubuntu/workspace/nextjs-serveractions-jest/app/action.ts:1:1]

I think the issue is related to the fact that server actions are transformed into serverless functions. So it is quite difficult doing a unit testing for those

I suggest you to implement e2e testing tools like cypress or playwright for this job You could then make a separate function with the logic so as to test it with jest

Yay! v13.5.4-canary.6 fixes it for me as well.

The test is now successful. Thank you!

@nicksan222 Thank you! But I got the same result. https://github.com/mkizka/nextjs-serveractions-jest/pull/1

$ cat jest.config.mjs 
import nextJest from "next/jest.js";

const createJestConfig = nextJest({
  // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
  dir: "./",
});

// Add any custom config to be passed to Jest
/** @type {import('jest').Config} */
const config = {
  // Add more setup options before each test is run
  // setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  testEnvironment: "node",
};

// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
export default createJestConfig(config);

$ yarn test
yarn run v1.22.19
$ jest
- warn You have enabled experimental feature (serverActions) in next.config.js.
- warn Experimental features are not covered by semver, and may cause unexpected or broken application behavior. Use at your own risk.

 FAIL  app/action.test.ts
  ● Test suite failed to run


      × To use Server Actions, please enable the feature flag in your Next.js config. Read more: https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions#convention
       ╭─[/home/ubuntu/workspace/nextjs-serveractions-jest/app/action.ts:1:1]