Examining the creation of mongoose model instances with Jest

Exploring the functionality of a REST API created with express and mongoose, I am utilizing jest and supertest for handling http calls. Despite being new to testing in javascript, I am eager to learn.

Specifically when testing a creation URL, my goal is to ensure that the instantiation is done solely using the req.body object. After researching about mock objects versus stubs and studying Jest documentation, my latest attempt resembles the following:

test('Should correctly instantiate the model using req.body', done => {

  const postMock = jest.fn();

  const testPost = {
    name: 'Test post',
    content: 'Hello'
  };

  postMock.bind(Post); // <- Post represents my model

  // Mocking the save function to avoid database usage
  Post.prototype.save = jest.fn(cb => cb(null, testPost));

  // Making the Supertest call
  request(app).post('/posts/')
  .send(testPost)
  .then(() => {
    expect(postMock.mock.calls[0][0]).toEqual(testPost);
    done();
  })
  .catch(err => {throw err});

});

Furthermore, I am interested in understanding how to deliberately fail a test upon promise rejection, preventing the error message

Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
from appearing.

Answer №1

Currently, you seem to be conducting more of an integration test rather than focusing on isolating the route handler function and testing it independently.

To improve this, I recommend separating the handler for /posts/ into its own file:

controllers/post-controller.js

const Post = require('./path/to/models/post')

exports.store = async (req, res) => {
  const post = await new Post(req.body).save()
  res.json({ data: post }
}

Then simply integrate the handler where you have defined your routes:

const express = require('express')
const app = express()
const postController = require('./path/to/controllers/post-controller')

app.post('/posts', postController.store)

With this separation, we can now focus on testing our postController.store specifically with req.body. To mock mongoose and avoid actual database interactions, create a mocked version of Post as follows (building upon your existing code):

path/to/models/__mocks__/post.js

const post = require('../post')

const mockedPost = jest.fn()
mockedPost.bind(Post)

const testPost = {
  name: 'Test post',
  content: 'Hello'
}


Post.prototype.save = jest.fn(cb => {
  if (typeof cb === 'function') {
    if (process.env.FORCE_FAIL === 'true') {
      process.nextTick(cb(new Error(), null))
    } else {
      process.nextTick(cb(null, testPost))
    }
  } else {
    return new Promise((resolve, reject) => {
      if (process.env.FORCE_FAIL === 'true') {
        reject(new Error())
      } else {
        resolve(testPost)
      }
    })
  }
})

module.exports = mockedPost

Note the check for process.env.FORCE_FAIL in case you wish to force a failure.

You are now prepared to test the functionality using req.body:

post-controller.test.js

// Loads anything contained in `models/__mocks__` folder
jest.mock('../location/to/models')

const postController = require('../location/to/controllers/post-controller')

describe('controllers.Post', () => {
  /**
   * Mocked Express Request object.
   */
  let req

  /**
   * Mocked Express Response object.
   */
  let res

  beforeEach(() => {
    req = {
      body: {}
    }
    res = {
      data: null,
      json(payload) {
        this.data = JSON.stringify(payload)
      }
    }
  })

  describe('.store()', () => {
    test('should create a new post', async () => {
      req.body = { ... }
      await postController(req, res)
      expect(res.data).toBeDefined()

      ...
    })

    test('fails creating a post', () => {
      process.env.FORCE_FAIL = true
      req.body = { ... }

      try {
        await postController.store(req, res)
      } catch (error) {
        expect(res.data).not.toBeDefined()

        ...
      }
    })

  })
})

This code is untested, but I hope it provides clarity for your testing efforts.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilizing the scrollTop method to display the number of pixels scrolled on

My goal is to show the number of pixels a user has scrolled on my website using the scrollTop method in jQuery. I want this number of pixels to be displayed within a 'pixels' class. Therefore, I plan to have <p><span class="pixels"> ...

Can OR be utilized within a find operation?

I am currently developing a social media platform similar to Facebook using Express and MongoDB. One of the features I'm working on is adding friends to user profiles. When a user clicks on a button that says "Send Friend Request" on another user&apos ...

Generating directory for application, only to find TypeScript files instead of JavaScript

While following a tutorial on setting up a react.js + tailwindcss app, I used the command npx create-next-app -e with-tailwindcss [app name]. However, instead of getting javascript files like index.js, I ended up with TypeScript files like index.tsx. You c ...

I have an array in JavaScript containing data objects within curly braces, and I want to disregard it

In trying to sum all values for each key in an array and create a new array with the key-total pairs, encountering difficulties when dealing with empty data represented by {}. To address this issue, attempting to validate that the data is not equal to {} b ...

Can the widthSegments parameter in SphereGeometry be dynamically adjusted?

Within my Three.JS project, I am utilizing a Sphere Geometry that is defined as follows: var radius = 1 ; var widthSegments = 12; var heightSegments = 12; var geometry = new THREE.SphereGeometry(radius, widthSegments, heig ...

Challenges arise when using node Serialport for writing data

My current project involves sending commands from a node server to an Arduino Mega board and receiving responses. Everything works smoothly when I limit the calls to SERIALPORT.write to once every 1000ms. However, if I attempt to increase the frequency, I ...

Resolve problems with implementing dynamic routes in Next.js

I have been learning about Next.js and I am struggling with understanding how to set up dynamic routing. I have the following setup: https://i.stack.imgur.com/uBPdm.png https://i.stack.imgur.com/YYSxn.png "use client" import React from "reac ...

Text included in the body of an email sent through Outlook

Whenever I click on a specific button, it opens Outlook with the provided details. Additionally, there is a TEXTAREA element containing certain texts. Is there a way to display this text in the body of my Outlook email? Below is the sample code - & ...

How can you trigger a link click event when clicking anywhere on the page using Jquery?

Here's the code I'm working with: <a href="http://google.com" target="_blank">Open in new tab </a> I am trying to make it so that when a user clicks anywhere on the website, the link above will be automatically clicked and a new tab ...

java printwriter not cooperating with javascript

Hey there, I'm currently working on a webpage created from a servlet using PrintWriter. I'm adding HTML, CSS, and JavaScript to the page, but unfortunately, the JavaScript functions are not functioning properly. Could this be due to a syntax erro ...

In my situation, I am unable to simultaneously use both the $set operator and $inc operator. It seems that only one

const User = require('./model.js') const result = await User.updateMany({_id:{$in:team}},{ $set: { current_level_status : 'incomplete' , user_helper_status : false,user_admin_status : false }},{$inc:{level_number:1}}); console.log( ...

Elegant routing using total.js (such as 'basepath/@username')

After working with Ruby and RoR, I'm diving into node.js for the first time. I want to create a user view in total.js with clean routing similar to how it's done in Rails. In Rails, I would use code like this: get '@:username' => ...

I'm running into an InvalidSelectorError and I could use some assistance in properly defining

As I gaze upon a massive dom tree, my task using NodeJS/Selenium is to locate an element by the title attribute within an anchor tag and then click on the associated href. Despite being a newcomer to regex, I am encountering numerous errors already. Below ...

Receiving blank response when trying to access server variable on the client side

Query: On the server side, I set the value of SessionData(EmployeeID) = "12345", which is first executed during page_load. Later, on the client side: function getEmployeeId() { return "<%# SessionData("EmployeeID")%>"; } When I use th ...

"Take your nodejs jade templates to a global level with international

I'm working on internationalizing my nodejs express app with the i18n-2 module. Everything is functioning correctly, but I have a question about translating strings in my jade templates. If I have a large number of strings on my website, do I need to ...

ReactJs not responding to rotation feature

System: Operating System: Windows 7 - 64 bit I am attempting to manipulate the rotation of a div in Reactjs by using click and drag mouse events similar to this example written in javascript code, which works flawlessly. I created a ReactJs applicatio ...

The Docker container seems to be unable to locate the index.js file within Node

I am attempting to optimize my express.js app by using a two-step process for building the image. Dockerfile: FROM node:alpine AS build WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build # Second step: FROM node:alpine WORKDIR / ...

I'm looking to learn how to efficiently write file chunks from a video upload in Node Js. How can I

My current project involves attempting to stream webcam or audio data to Node.js and save it on disk. The aim is to send the chunks of data to the server as soon as they are available. I have successfully captured the stream using getUserMedia, set up me ...

Discovering the highest value within an array of objects

I have a collection of peaks in the following format: peaks = 0: {intervalId: 7, time: 1520290800000, value: 54.95125000000001} 1: {intervalId: 7, time: 1520377200000, value: 49.01083333333333} and so on. I am looking to determine the peak with the hig ...

Managing variables or properties that are not defined in ES6

Currently, I am utilizing Angular and Firebase Firestore in my project. However, the question posed could be relevant to Typescript and pure Javascript as well. The functionality of my application relies heavily on the data retrieved from Firestore. A sim ...