Bird
Raised Fist0
NextJSframework~10 mins

Testing server actions in NextJS - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - Testing server actions
Write server action function
↓
Mock dependencies if needed
↓
Call server action in test
↓
Check returned result or side effects
↓
Pass or fail test
↓
End
This flow shows how to write and test a Next.js server action by calling it and checking its result or side effects.
Execution Sample
NextJS
export async function addUser(data) {
  // pretend to save user
  return { success: true, user: data };
}

// Test
(async () => {
  const result = await addUser({ name: 'Ana' });
  console.log(result);
})();
This code defines a server action that adds a user and returns success, then tests it by calling and logging the result.
Execution Table
StepActionInputOutputNotes
1Call addUser{ name: 'Ana' }Pending PromiseFunction starts, async call begins
2Inside addUser{ name: 'Ana' }Prepare return objectSimulate saving user
3Return result{ name: 'Ana' }{ success: true, user: { name: 'Ana' } }Promise resolves with result
4Test receives resultAwaited result{ success: true, user: { name: 'Ana' } }Test can now check output
5Log outputResult objectPrinted to consoleShows test output
6Test assertionResult objectPass if matches expectedTest passes if output is correct
💡 Test ends after checking the returned result matches expected output.
Variable Tracker
VariableStartAfter Step 2After Step 3After Step 4Final
dataundefined{ name: 'Ana' }{ name: 'Ana' }{ name: 'Ana' }{ name: 'Ana' }
resultundefinedundefinedPromise pending{ success: true, user: { name: 'Ana' } }{ success: true, user: { name: 'Ana' } }
Key Moments - 2 Insights
Why is the result a Promise at first and not the final object?
Because addUser is async, it returns a Promise immediately (see Step 1 and 3). The Promise resolves later with the final object (Step 3).
How do we check the result of the server action in the test?
We use await to get the resolved value from the Promise (Step 4), then compare it to the expected output (Step 6).
Visual Quiz - 3 Questions
Test your understanding
Look at the execution table, what is the value of 'result' after Step 3?
AA pending Promise
BThe final user object
CUndefined
DAn error
💡 Hint
Check the 'result' variable in variable_tracker after Step 3 and the output column in execution_table Step 3.
At which step does the test receive the resolved result from the server action?
AStep 2
BStep 4
CStep 1
DStep 6
💡 Hint
Look at execution_table Step 4 where the test awaits the result.
If the server action returned an error instead of success, which step would change?
AStep 5 logging
BStep 1 input
CStep 3 output
DStep 6 test assertion
💡 Hint
Errors affect the returned output, so check Step 3 output in execution_table.
Concept Snapshot
Testing server actions in Next.js:
- Write async server action functions
- Call them in tests using await
- Check returned results or side effects
- Mock dependencies if needed
- Use assertions to confirm expected behavior
Full Transcript
This visual execution shows how to test Next.js server actions. First, you write an async function that performs a server task and returns a result. When testing, you call this function with test data. Because it is async, it returns a Promise initially. You await this Promise to get the final result. Then you check if the result matches what you expect. This process helps confirm your server actions work correctly before deploying.

Practice

(1/5)
1. What is the main purpose of testing server actions in Next.js?
easy
A. To catch bugs early and ensure backend reliability
B. To improve frontend styling and layout
C. To speed up client-side rendering
D. To reduce the size of JavaScript bundles

Solution

  1. Step 1: Understand server actions role

    Server actions handle backend logic and data processing in Next.js.
  2. Step 2: Identify testing benefits

    Testing server actions helps find bugs early and keeps backend code reliable.
  3. Final Answer:

    To catch bugs early and ensure backend reliability -> Option A
  4. Quick Check:

    Testing server actions = catch bugs early [OK]
Hint: Testing server actions = backend reliability focus [OK]
Common Mistakes:
  • Confusing server action testing with frontend styling
  • Thinking testing speeds up rendering
  • Assuming testing reduces bundle size
2. Which of the following is the correct way to import a server action for testing in Next.js using Vitest?
easy
A. require('myAction') from '@/app/actions';
B. const myAction = import('@/app/actions');
C. import myAction from 'vitest';
D. import { myAction } from '@/app/actions';

Solution

  1. Step 1: Recall ES module import syntax

    Next.js and Vitest use ES modules, so import uses 'import { name } from path'.
  2. Step 2: Check each option

    import { myAction } from '@/app/actions'; uses correct syntax. require('myAction') from '@/app/actions'; mixes require and import wrongly. import myAction from 'vitest'; imports from wrong module. const myAction = import('@/app/actions'); uses dynamic import incorrectly for testing.
  3. Final Answer:

    import { myAction } from '@/app/actions'; -> Option D
  4. Quick Check:

    Correct ES import syntax = import { myAction } from '@/app/actions'; [OK]
Hint: Use ES module import syntax for server actions [OK]
Common Mistakes:
  • Using CommonJS require instead of import
  • Importing from wrong module
  • Using dynamic import without await
3. Given this server action test code snippet, what will be the output?
import { describe, it, expect } from 'vitest';
import { addNumbers } from '@/app/actions';

describe('addNumbers', () => {
  it('adds two numbers correctly', () => {
    const result = addNumbers(2, 3);
    expect(result).toBe(5);
  });
});
medium
A. Test fails because addNumbers returns a string
B. Test fails because addNumbers is asynchronous
C. Test passes because 2 + 3 equals 5
D. Test throws syntax error due to missing async

Solution

  1. Step 1: Analyze the test function

    The test calls addNumbers(2, 3) and expects 5.
  2. Step 2: Check function behavior

    If addNumbers adds numbers synchronously, result is 5, so test passes.
  3. Final Answer:

    Test passes because 2 + 3 equals 5 -> Option C
  4. Quick Check:

    2 + 3 = 5 test passes [OK]
Hint: Check if function returns expected value synchronously [OK]
Common Mistakes:
  • Assuming function is async without evidence
  • Confusing return types
  • Expecting syntax errors without cause
4. You wrote this test for a server action but it fails with a runtime error. What is the likely cause?
import { it, expect } from 'vitest';
import { fetchData } from '@/app/actions';

it('fetches data successfully', () => {
  const data = fetchData();
  expect(data).toHaveProperty('id');
});
medium
A. expect syntax is invalid for property check
B. fetchData is asynchronous but test lacks async/await
C. fetchData is not imported correctly
D. vitest cannot test server actions

Solution

  1. Step 1: Identify function type

    fetchData likely returns a Promise because it fetches data asynchronously.
  2. Step 2: Check test function

    Test does not use async/await, so it tries to check a Promise directly, causing runtime error.
  3. Final Answer:

    fetchData is asynchronous but test lacks async/await -> Option B
  4. Quick Check:

    Async function needs async/await in test [OK]
Hint: Async server actions need async/await in tests [OK]
Common Mistakes:
  • Forgetting async keyword in test
  • Checking Promise directly without await
  • Assuming import error without evidence
5. You want to test a server action that creates a user and returns the user ID. The action throws an error if the username is empty. Which test cases should you write to cover both success and error scenarios?
hard
A. Test with a valid username expecting an ID, and test with empty username expecting an error
B. Test only with a valid username expecting an ID
C. Test with empty username expecting a success response
D. Test with invalid username expecting a null return

Solution

  1. Step 1: Identify success scenario

    Valid username should create user and return ID, so test this case.
  2. Step 2: Identify error scenario

    Empty username causes error, so test that error is thrown properly.
  3. Final Answer:

    Test with a valid username expecting an ID, and test with empty username expecting an error -> Option A
  4. Quick Check:

    Cover success and error cases in tests [OK]
Hint: Always test success and error cases for server actions [OK]
Common Mistakes:
  • Testing only success cases
  • Expecting success on invalid input
  • Ignoring error throwing behavior
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard