Testing the Mongoose save() method by mocking it in an integration test

I am currently facing an issue while trying to create a test scenario. The problem arises with the endpoint I have for a REST-API:

Post represents a Mongoose model.

router.post('/addPost', (req, res) => {
  const post = new Post(req.body);
  post.save()
    .then(() => ... return success)
    .catch((err) => ... return error);
});

My goal is to write a test that verifies if the correct value is returned when save() resolves.

After attempting to mock the save() method within the model, I realized it may not be possible. As mentioned in this thread , save is not a method on the model itself but on the document (an instance of the model).

Even after following the recommended approach from the above-mentioned discussion, I seem to be missing something as I cannot successfully override the save() method.

The code snippet I have so far (factory is sourced from factory-girl as per the instructions provided in the given link. Here is a direct link to the package):

factory.define('Post', Post, {
  save: new Promise((resolve, reject) => {
    resolve();
  }),
});
factory.build('Post').then((factoryPost) => {
  Post = factoryPost;
  PostMock = sinon.mock(Post);
  PostMock.expects('save').resolves({});

  ... perform test
});

I believe my implementation might be off track, but I am unable to pinpoint where I am going wrong. There must be others who have encountered a similar issue before.

Answer №1

If you want to create a stub for the post document, which represents an instance of the Post model, you can achieve this by stubbing the Post.prototype.

For example:

server.ts:

import express from 'express';
import Post from './models/post';
import http from 'http';

const app = express();
app.post('/addPost', async (req, res) => {
  const post = new Post(req.body);
  await post.save();
  res.sendStatus(200);
});

const server = http.createServer(app).listen(3000, () => {
  console.info('Http server is listening on http://localhost:3000');
});

export { server };

models/post.ts:

import { model, Schema } from 'mongoose';

const postSchema = new Schema({
  titie: String,
});

export default model('post', postSchema);

server.integration.spec.ts:

import { server } from './server';
import request from 'supertest';
import Post from './models/post';
import sinon from 'sinon';
import { expect } from 'chai';

after((done) => {
  server.close(done);
});

describe('58820254', () => {
  it('/addPost', (done) => {
    const saveStub = sinon.stub(Post.prototype, 'save').returnsThis();
    request(server)
      .post('/addPost')
      .expect(() => {
        console.log('assert');
        expect(saveStub.calledOnce).to.be.true;
      })
      .expect(200, done);
  });
});

Integration test with full coverage:

Http server is listening on http://localhost:3000
  58820254
assert
    ✓ /addPost


  1 passing (25ms)

-----------------------------|----------|----------|----------|----------|-------------------|
File                         |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-----------------------------|----------|----------|----------|----------|-------------------|
All files                    |      100 |      100 |      100 |      100 |                   |
 58820254                    |      100 |      100 |      100 |      100 |                   |
  server.integration.spec.ts |      100 |      100 |      100 |      100 |                   |
  server.ts                  |      100 |      100 |      100 |      100 |                   |
 58820254/models             |      100 |      100 |      100 |      100 |                   |
  post.ts                    |      100 |      100 |      100 |      100 |                   |
-----------------------------|----------|----------|----------|----------|-------------------|

Source code: https://github.com/mrdulin/mongoose5.x-lab/tree/master/src/stackoverflow/58820254

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

Suggestions for resetting the input field IDs in a cloned div after deletion

In the given HTML code snippet, there is a <div> containing two input fields as shown below: <button type = "button" id = "add-botton" >Add Element </button> <div id = "trace-div1" class = "trace"> <h4><span>Trac ...

What is the best way to link one element to another element?

Apologies for the mismatched title, but here's my issue: I need to create hidden checkboxes for each Polymer's paper-button div in jsp/style related problems. <%int i; %> <form ACTION="Computation.jsp"> <% for(i=0; i<n; ...

Angular's alternative to JQUERY for handling file uploads and multipart forms

My multipart form includes a simple file upload: I would like to customize it to appear like this: Although my initial solution works well, here is the code snippet: HTML snippet <div> <input type="file" name="file" onchange="angular.eleme ...

Creating a variable by using a conditional operation in JavaScript

When the statement <code>name = name || {} is used, it throws a reference error. However, using var name = name || {} works perfectly fine. Can you explain how variable initialization in JavaScript functions? ...

ReactJS fetching previous data through POST request

I am facing an issue while trying to replicate Google Translate. I have an API that sends and returns data as expected during the first translation attempt. However, after changing the text and attempting another translation, it sends the updated text but ...

Exceeded Limit: Google Maps API cannot process more than 10 destinations at this time

After spending the entire day researching, I am still unable to find a solution that solves my issue. I am utilizing the Google Maps Distance Matrix Service with 1 origin and 14 destinations. While testing the modified sample code from Google (https://deve ...

How to stop Accordion from automatically collapsing when clicking on AccordionDetails in Material-UI

I am working on a React web project with two identical menus. To achieve this, I created a component for the menu and rendered it twice in the App component. For the menu design, I opted to use the Material UI Accordion. However, I encountered an issue wh ...

Phantom.js: Exploring the Power of setTimeout

Below is a snippet of code that intends for Phantom.js to load a page, click on a button, and then wait for 5 seconds before returning the HTML code of the page. Issue: Utilizing setTimeout() to introduce a delay of 5 seconds leads to the page.evaluate fu ...

es-lint is issuing a warning about the presence of multiple modules within the node_modules directory that have names differing only in their casing

After reviewing all my import statements, I found that everything looks correct. The only unusual import I have is react-bootstrap, which I import as: import { Jumbotron, Button } from 'react-bootstrap'; I'm using the Jumbotron and Button ...

The damping effect in three.js OrbitControls only activates when the mouse is pressed, however there is no damping effect once the

I find it difficult to articulate: Currently, I am utilizing OrbitControls in three.js and have activated damping for a smoother rotation with the mouse. It is somewhat effective but not entirely seamless. When I click and drag, the damping feature works ...

Socket.io is most effective when reconnecting

I am currently in the process of developing a React application that connects to a Node.js API and I am trying to integrate the Socket.io library. Following various online tutorials, my code now looks like this: API: import express from 'express&apo ...

Syncing data between parent and child components in VueJS using v-bind:sync with an object parameter for bidirectional data flow

In the app I am developing, there is a main component that passes an Object, specifically this.form, to a child form component using v-bind:sync="form". The child form then emits values for each key of the parent object on @input events. My goal is to be ...

I'm stumped trying to understand why I keep getting this syntax error. Any thoughts on how to fix it?

Our team is currently working on creating a dynamic SELECT box with autocomplete functionality, inspired by the Standard Select found at this link: We encountered an issue where the SELECT box was not populating as expected. After further investigation, ...

Do you think it's feasible to configure cookies for express-session to never expire?

Is there a way to make cookies never expire for express-session? If not, what is the maximum maxAge allowed? I came across some outdated information on setting cookie expiration on SO (over 10 years old) and here on express, which mentions a maxAge of 1 y ...

Information is inaccessible beyond onBeforeMount in the Composition API of Vue 3

In my code snippet block, I have the following code: <script setup lang="ts"> import ApiService from '../service/api' import { reactive, onBeforeMount } from 'vue' let pokemons = reactive([]) onBeforeMount(async ()=> ...

Checking and merging arrays in Javascript

I have a unique challenge involving two arrays of objects that I need to merge while excluding any duplicates. Specifically, if an object with the key apple: 222 already exists in the first array, it should be excluded from the second array. Please see be ...

Is it possible to integrate a Backbone app as a component within a separate app?

I am currently in the process of familiarizing myself with Backbone.js and front-end development, as my background is more focused on back-end development. I have a question related to composition. Imagine that I need to create a reusable component, like ...

Combining actions in a chain within an NgRx effect for Angular

After successfully working on an effect, I now face the challenge of chaining it with a service called in a subsequent action after updating the state in the initial action through a reducer. Here is the effect code: @Effect() uploadSpecChange$: Observab ...

Integrating Dynamics CRM with an External Source to Trigger Workflows

Scenario: Imagine a scenario where you want to trigger an existing workflow or a custom action from a webpage located outside the CRM Dynamics environment, such as MS CRM 2011-2013-2015-2016 and 365. Potential Solution: One possible solution could be to ...

What is the purpose of the assertEquals() method in JSUnit?

Currently, I am exploring unit test exercises with a HTML5/JS game that I created and JSUnit test runner. The simplicity of the setup impresses me, but I have noticed that even the documentation lacks a clear explanation of what assertEquals() truly does. ...