Reputation: 765
I'm trying to mock a node module so that it will return mocked data. However in my tests it is still making an actual call. Considering the following code:
const makeRequestStructure = require('./modules/makeRequestStructure.js').makeRequestStructure
const normalizeFinalResponse = require('./modules/normalizeFinalResponse.js').normalizeFinalResponse
const doARequest = require('./modules/doARequest.js').doARequest
exports.addPost = (event) => {
const requestStructure = makeRequestStructure('POST', '/posts')
const requestPostData = {
title: event.body.title,
content: event.body.content
}
return doARequest(requestStructure, requestPostData).then((res) => {
const finalResponse = normalizeFinalResponse(200, res)
return finalResponse
}).catch((err) => {
const finalResponse = normalizeFinalResponse(400, err)
return finalResponse
})
}
I have the following test file:
const mock = require('mock-require')
const sinon = require('sinon')
const expect = require('chai').expect
const addPost = require('../addPost.js')
describe('the addPost API call', () => {
beforeEach(() => {
mock('../modules/doARequest', { doARequest: function() {
return Promise.resolve({})
}})
});
it('Returns with a statusCode of 200', () => {
const event = { body: { title: 'Lorem ipsum', content: 'Lorem ipsum dolor sit amet' } }
const expectedReturn = { id: 20000000000000 }
return addPost.addPost(event).then((res) => {
expect(res.body.message).to.eql(expectedReturn)
})
});
})
This test is making an actual call to https://jsonplaceholder.typicode.com/posts
and it returns { id: 101 }
. However i want it to return { id: 20000000000000 }
.
I have tried to mock the request using Nock. However the hostname is defined in a .env
file and could be different depending on the server the script runs on.
The doARequest.js
file looks like this:
const https = require('https')
module.exports.doARequest = function (params, postData) {
return new Promise((resolve, reject) => {
const req = https.request(params, (res) => {
let body = []
res.on('data', (chunk) => {
body.push(chunk)
})
res.on('end', () => {
try {
body = JSON.parse(Buffer.concat(body).toString())
} catch (e) {
reject(e)
}
resolve(body)
})
})
req.on('error', (err) => {
reject(err)
})
if (postData) {
req.write(JSON.stringify(postData))
}
req.end()
})
}
What am i doing wrong here? Any help will be appreciated.
Upvotes: 3
Views: 2777
Reputation: 24565
As an alternative to estus solution with reRequiring the module, you could use "dependency injection".
The problem is that you're directly depending on the doARequest.js
-module in addPost.js
, thus making it difficult to mock its behaviour. This is where dependency injection comes in handy, since you can just pass a mocked doARequest-module in your unit test:
// addPost.js
exports.addPost = (event, doARequest) => {
const requestStructure = makeRequestStructure('POST', '/posts')
const requestPostData = {
title: event.body.title,
content: event.body.content
}
return doARequest(requestStructure, requestPostData).then((res) => {
const finalResponse = normalizeFinalResponse(200, res)
return finalResponse
}).catch((err) => {
const finalResponse = normalizeFinalResponse(400, err)
return finalResponse
})
}
// addPost.test.js
describe('the addPost API call', () => {
it('Returns with a statusCode of 200', () => {
const mockedDoARequest = () => Promise.resolve({ "whateverResponseYouLike": "12345" });
const event = { body: { title: 'Lorem ipsum', content: 'Lorem ipsum dolor sit amet' } }
const expectedReturn = { id: 20000000000000 }
return addPost.addPost(event, mockedDoARequest).then((res) => {
expect(res.body.message).to.eql(expectedReturn)
})
});
})
Everywhere else you'd pass the real module, i.e.
const doARequest = require('./modules/doARequest.js').doARequest
...
return addPost.addPost(event, doARequest).then(...)
Upvotes: 1
Reputation: 223104
CommonJS modules produce singleton export objects. Once a module is imported, it isn't evaluated. mock(...)
doesn't affect anything because original doARequest
module was evaluated when addPost
was imported.
mock-require
provides a way to re-evaluate modules. addPost
shouldn't be imported at the top of test file. Instead, it should be imported inside a test where it's used:
const addPost = mock.reRequire('../addPost.js');
Upvotes: 3