How to mock @shopify/shopify-api post method using Jest

I’m working with the @shopify/shopify-api package and need to create unit tests using Jest. My main issue is mocking the REST client’s post method to return specific test data.

Here’s my current test setup:

describe('Shopify API tests', () => {
  beforeEach(() => {
    jest.useFakeTimers();
    jest.setTimeout(30000);
  });

  afterEach(() => {
    jest.clearAllMocks();
    jest.clearAllTimers();
  });

  test('should create product via shopify api', async () => {
    const restClient = new Shopify.Clients.Rest(
      testMocks.storeUrl,
      testMocks.token
    );
    restClient.post.mockResolvedValue(testMocks.expectedResult);
    
    const result = await createShopifyProduct(
      testMocks.storeUrl,
      testMocks.token,
      productMocks.testData
    );
    
    expect(result).toMatchObject(testMocks.expectedResult);
  });

  test('handles api errors correctly', async () => {
    global.fetch = () =>
      Promise.resolve({
        json: () => Promise.resolve(testMocks.errorResponse),
      });
    
    await expect(
      createShopifyProduct(testMocks.storeUrl, 'invalid_token', {})
    ).rejects.toThrow('API request failed');
  });
});

And here’s the function I’m trying to test:

export const createShopifyProduct = async (storeUrl, token, productData) => {
  try {
    const restClient = new Shopify.Clients.Rest(storeUrl, token);
    const apiResponse = await restClient.post({
      path: 'products',
      data: productData,
      type: DataType.JSON,
    });
    return apiResponse;
  } catch (error) {
    throw new ApiError(error);
  }
};

The problem is that after mocking the shopify-api module, all methods return undefined and I can’t override the post method to return my test data. The test fails with ‘received value must be a non-null object’ error.

What’s the correct way to mock the post method so it returns my mock data instead of undefined?

You’re not mocking the Shopify module early enough. Jest needs the mock in place before you create the Rest client instance, but you’re trying to mock the post method after.

Here’s what works in your test file:

const mockPost = jest.fn();

jest.mock('@shopify/shopify-api', () => ({
  Shopify: {
    Clients: {
      Rest: jest.fn().mockImplementation(() => ({
        post: mockPost
      }))
    }
  },
  DataType: { JSON: 'application/json' }
}));

Then in your test, just set the resolved value:

mockPost.mockResolvedValue(testMocks.expectedResult);

This sets up the mock at module level so your Rest client instance gets the mocked post method. Don’t forget to clear the mock between tests with mockPost.mockClear() in your afterEach block.