Search code examples
node.jsexpressjestjsmultersupertest

How to mock multer using jest/enzyme to file upload using axios post mock call


I am testing my express router with axios post-call to backend. I am getting 500 responses instead of 200, not sure how to mock the multer effectively. Any thoughts on this? Thanks

routes.jsx

const axios = require('axios')
const router = express.Router()
const multer = require('multer')
const FormData = require('form-data')
const express = require('express')

const upload = multer({ storage: multer.memoryStorage() }).any()

router.post('/', upload, (req, res) => {
  const formData = new FormData()
   const { body } = req 
    req.files.forEach(file => {
      formData.append(
        'files',
        file.buffer,
        {
          filename: file.originalname
        },
        file.originalname
      )
    })


  axios
    .post('/api/endpoint', formData)
    .then(response => {return response
    })
    .catch(e => {
    console.log(e)
    })
})

module.exports = router

Below are my test case

routes.jsx.test

const axios = require('axios')
const MockAdapter = require('axios-mock-adapter')
const myroute = require('myroute')
const app = express()
const mock = new MockAdapter(axios)
const request = require('supertest')
const express = require('express')
const bodyParser = require('body-parser')
const multer = require('multer')
jest.mock('multer')

multer.mockImplementation(() => {
  return {
    any () {
      return (req, res, next) => {
        req.body = { userName: 'testUser' }
        req.files = [
          {
            originalname: 'sample.name',
            mimetype: 'sample.type',
            path: 'sample.url'
          }
        ]
        return next()
      }
    }
  }
})
app.use(bodyParser.json())

app.use('/', myroute)

describe('sendFiles', () => {
  const url = '/api/endpoint'  

  test('200 response', () => {
    const myMockRes = { mykey: 'myVal' }
    let formData = new FormData()
    const file = new Blob(['somee contents'], { type: 'multipart/form-data' })
    formData.append('files', file)
    formData.append('userName', 'testUser')
    mock.onPost(url).reply(200, myMockRes)
    return (
      request(app)
        .post('/')
        .send({ userName: 'testUser', files: [file] })
        //.expect('Content-Type', /json/)
        .expect(200)
        .then(response => {
          const { data } = response.body
          expect(data).toEqual(myMockRes)
        })
    )
  })


})

error:

TypeError: Cannot read property 'any' of undefined in routes.jsx

const upload = multer({ storage: multer.memoryStorage() }).any()
    

Solution

  • When you use jest.mock('multer'), Jest automatically mocks the module and returns undefined when it gets called in the test. Since we want to mock memoryStorage and any methods as well, we have to do it explicitly by passing a factory as the second argument to jest.mock.

    jest.mock('multer', () => {
      const multer = () => ({
        any: () => {
          return (req, res, next) => {
            req.body = { userName: 'testUser' }
            req.files = [
              {
                originalname: 'sample.name',
                mimetype: 'sample.type',
                path: 'sample.url',
                buffer: Buffer.from('whatever'), // this is required since `formData` needs access to the buffer
              },
            ]
            return next()
          }
        },
      })
      multer.memoryStorage = () => jest.fn()
      return multer
    })
    

    The other issue is that Blob does not exist in Node. You can use Buffer.from to generate a buffer to send in the request.

    const file = Buffer.from('whatever')
    

    And you don't need to use FormData in the test.

    The whole code:

    // router.test.js
    
    const axios = require('axios')
    const MockAdapter = require('axios-mock-adapter')
    const express = require('express')
    const app = express()
    const mock = new MockAdapter(axios)
    const request = require('supertest')
    
    const bodyParser = require('body-parser')
    
    const myroute = require('./router')
    
    jest.mock('multer', () => {
      const multer = () => ({
        any: () => {
          return (req, res, next) => {
            req.body = { userName: 'testUser' }
            req.files = [
              {
                originalname: 'sample.name',
                mimetype: 'sample.type',
                path: 'sample.url',
                buffer: Buffer.from('whatever'),
              },
            ]
            return next()
          }
        },
      })
      multer.memoryStorage = () => jest.fn()
      return multer
    })
    
    app.use(bodyParser.json())
    
    app.use('/', myroute)
    
    describe('sendFiles', () => {
      const url = '/api/endpoint'
    
      test('200 response', () => {
        const myMockRes = { mykey: 'myVal' }
        const file = Buffer.from('whatever')
        mock.onPost(url).reply(200, myMockRes)
        return request(app)
          .post('/')
          .send({ userName: 'testUser', files: [file] })
          .expect(200)
          .then((response) => {
            const { data } = response.body
            expect(data).toEqual(myMockRes)
          })
      })
    })