[SalesForce] LWC + Jest: Mocking imperative Apex and returning a value

I'm going to do a Q&A-style entry here, since I couldn't find anything on the web describing this use case. I have seen several threads here describing how to do an empty mock on an Apex call in LWC using Jest, like this:

import insertRecords from '@salesforce/apex/MyController.insertRecords';

jest.mock(
    '@salesforce/apex/MyController.insertRecords',
    () => {
        return {
            default: jest.fn()
        };
    },
    { virtual: true }
);

I want my mock Apex call to return a value. In this example, I'm inserting records and returning those inserted records. In order to fully test my component, the Apex stub needs to return the same array of objects it received, but with the Id property populated.

Best Answer

For easier readability, you can store as many .json files as needed for all your mocked data needs and have "less" in your jest test.

Within your __tests__ directory, you can create a data folder and then store the .json file of your mocked data within it. File structure would look like so:

force-app
└───main
    └───default
        └───lwc
            └───lwcComponentName
                └───__tests__
                    |   lwcComponentName.test.js
                    └───data
                        |   insertedRecordsMock.json
                        |   insertedRecordsErrorMock.json

Your .json file would just be the response you get from your call. This is helpful for responses you want to mock that may be large

{
  "apiName": "Account",
  "fields" : {
    "Name": "Local Boxes",
    "BillingState": "WA",
    "BillingStreet" : "123 Main Street",
    "BillingCountry" : "USA"
  }
}

Within a given jest test, you'll have all the imports needed at the top (in this example, your apex method you call imperatively).

import insertRecords from '@salesforce/apex/MyController.insertRecords';

Load as many files in separate constants as you need for all your tests. In this example, we have mock data for a happy path and failure path

const mockInsertedRecords = require("./data/insertedRecordsMock.json");
const mockErrorInsertedRecords = require("./data/insertedRecordsErrorMock.json");

Mock the imperative apex call - we return a new, unused mock function so we can pick what the return will look like within the specific test.

jest.mock(
    "@salesforce/apex/MyController.insertRecords",
    () => {
        return {
            default: jest.fn(),
        };
    },
    { virtual: true }
};

Then, in your actual specific tests - supply whatever data that should be returned for the mock of your call depending on your testing scenario. Below is the happy path as mockResolvedValue really just mocks the Promise.resolve() which is the happy path of your apex imperative call.

insertedRecords.mockResolvedValue(mockInsertedRecords);

For the failure scenario, you want to mock what happens if your promise is rejected so we can utilize mockRejectedValue()

insertedRecords.mockRejectedValue(mockErrorInsertedRecords);
Related Topic