Testing functions that have database calls with Jest Testing functions that have database calls with Jest mongoose mongoose

Testing functions that have database calls with Jest


Here is a simple working example based on the code provided:


code.js

const { Message } = require('./message');function buildText(listing, messageRule, reservation, isReview = false, isLastMinuteMessage = false) {  return new Promise(async function (resolve) {    let message, text;    const name = reservation.name;    try {      message = await Message.findOne({        listingID: listing._id,        messageRuleID: messageRule._id,        reservationID: reservation._id,        message: { $exists: true }      });    } catch (error) {      console.error(error);    }    if (message) {      text = message.message;    } else {      text = messageRule.message;      if (isLastMinuteMessage) {        text = messageRule.lastMinuteMessage;      }    }    text = text.replace(/{{Guest Name}}/g, name);    resolve(text);  });}module.exports = {  buildText}

code.test.js

const Code = require('./code');const { Message } = require('./message');describe("funcs", () => {  it("buildText", async () => {    const mock = jest.spyOn(Message, 'findOne');  // spy on Message.findOne()    mock.mockImplementation(() => Promise.resolve({      message: 'the message'    }));  // replace the implementation    let listing = {      _id: "1324",      pricingEnabled: true,      listingFound: true,      hideListing: null    };    let messageRule = {      _id: "3452345",      minNights: 1,      reviewEnabled: false,      disableMessageAfterReview: false,      message: 'test',      lastMinuteMessage: 'test2'    };    let reservation = {      _id: "63452345",      hasLeftReview: true,      hasIssues: false    };    let isReview = false;    let isLastMinuteMessage = false;    let response = await Code.buildText(listing, messageRule, reservation, isReview, isLastMinuteMessage);    expect(response).toMatchSnapshot();    mock.mockRestore();  // restore Message.findOne()  });});

message.js (dummy model for the example)

const mongoose = require('mongoose');exports.Message = mongoose.model('Message', new mongoose.Schema({ }));


I'm not 100% sure about buildText (looks good to me though I never used new Promise(async ...)) - so I focus on the test: you are doing the essentials right as far as I can see. only thing is that you did not

// jest.mock('mongoose'); doesn't work, unfortunatelyconst mReal = require('mongoose');const mMock = mReal; 

then add a beforeAll like

    beforeAll(() => { // not 110% clean, but I'm not alone: https://github.com/facebook/jest/issues/936#issuecomment-214556122        mMock.model = jest.fn(); // manually overwrite one fct with a mock    });

At the very first line of code.test.js; plus: you need to train the mock then, i.e. at the beginning if it insert

    it("...", () => {        const findOneMock = jest.fn();        findOneMock.mockReturnValue(Promise.resolve({message: "hey"}));        mMock.model.mockReturnValue({findOne: findOneMock});        // prep done, test something

I used some simple test code, I think your code will do an equivalent:

        let res = await mMock.model('whatever').findOne('whatever');        expect(res.message).toBe("hey");    });

Hope I got all hidden specifics (like how you come about Message) right - but per, you should now actually resolve to something.


if you are using jest, then the best approach is to define a file called setup.js which runs before any of your test files get run, then in this file, you should import your mongoose models and then make a connection to your test database. (this is because your test files are running separately from your server files and do not know anything about your mongoose models or your database connection, so by defining this file and making connections, you are defining mongoose for your test environment and can use it as you want without getting null)

you need to go through three steps:

  1. first in your package.json in order to tell jest to run contents of a file before any other test file gets run, you need to add this block before your scripts block:

    "jest": {  "setupTestFrameworkScriptFile": "./tests/setup.js"}
  2. then you need to require in your models from mongoose and the mongoose library in your setup.js file and then make a connection to your databse:

    require('../models/yourModel'); //require in any models that you need to use in your testsconst mongoose = require('mongoose');mongoose.connect(mongoURI,{useMongoClient: true});mongoose.connection.once('open',()=>{}).on('error',(err)=> {console.log(err)});
  3. then you can use your model in your test files to run or test any queries from mongoose, for Example, if you want to create a record of a model called Model in your test file:

    const mongoose = require('mongoose');const Model = mongoose.model('Model');const newRecord = await new Model({}).save();//test newRecord