简体   繁体   中英

mocking database calls in api routes

I am building a full stack application with NextJS and a MySQL database.In my API routes I make calls to this database (for example, createOne() for creating an instance in the database). When I run my tests using jest, I want to mock this call, so that the actual createOne() does not run, but only test so that it is called. From what I've understood this is possible using mocks, but my tests still runs using the real function.

lib/database.js

const createConnection = async () => {}; // creates the connection
export const createOne = async (instance) => {}; // inserts the instance to the database

pages/api/index.js

import { createOne } from 'lib/database';
const handler = () => {
  ... // validation, creating the instance to insert etc.
  const id = await createOne(instance); // <-- this I don't want to be run when testing
  res.status(201).json({id, ...instance});
};
export default handler();

__tests__/api/index.js

import handler from 'pages/api/index';
import { createOne } from 'lib/database'; 
// I struggle how to structure the mock implementation here
beforeAll(() => {
  const createOne = jest.fn(); // ?
});
...
test(async () => {
  ... // setup req, res object
  await handler(req, res);
  // I have tests for the API logic, but it currently calls the database every time, which I don't want to.
  expect(createOne.mocks.calls.length).toBe(1); // here I get 0
});

I don't really know where I'm doing it wrong, and I have been searching around a lot but most examples are just mocking modules such as axios etc.

You don't seem to be mocking the function...

See below and see if it works...

  // mock function
    const createOneMock = jest.fn().mockImplementation(async (val) => 1)
    
    // Mock the module
    jest.mock("../lib/database", () => {
      return createOneMock
    })
    const handler = require("../index")
    
    test('my test', async () => {
      var req = {}
      var res = {}
      const rc = await handler(req, res);
      expect(rc).toBe(1)
      expect(createOneMock.mock.calls.length).toBe(1)
    })

ps: I'm using Node to test this, but the concept should be the same regardless if you are using node or not.

The way I got it working is some workarounds needed for jest to mock esModules. See below a working example:

import handler from 'src/pages/api/index';
import { createOne } from 'src/lib/database';

jest.mock('src/lib/database', () => ({
  __esModule: true,
  createOne: jest.fn().mockImplementation(async (val) => 1)
}));

test('it calls the mock and returns id: 1', async () => {
  const { req, res } = mockReqRes();  // using node-mocks-http, out of scope
  await handler(req, res);
  expect(createOne.mock.calls.length).toEqual(1);
  expect(res._getJSONData()).toEqual(
    expect.objectContaining({
      id: 1
    })
  );
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM