A very commonly asked question is “I want to mock an exported constant my JavaScript is importing from another file/module, how can I do this?”. This short tutorial should show how to do this in plain JavaScript.
Mocking exported constants/methods that are being imported in a JavaScript file is a use case that can appear more often than one thinks. One pretty common use case is that you e.g. have a module that abstracts away some logic to either access external services or databases.
When you do Unit Testing you NEVER want to do calls to external services or your database so you abstract away the logic for this into one module which you then mock in all your unit tests. Of course, there are also “global Mocks” in Jest you can set once for all your tests, but that's another story.
You can see the whole codebase of the below example in the following repository on GitHub:
For our example, let's imagine we have some main()
method which calls some external service and returns its result.
For Unit Testing, in this main()
method we have to mock the external service calls, triggered via callExternalService()
for several reasons:
- We don’t want to suddenly see our tests failing just because some external service going down or having problems. Failing tests could e.g. block deployment pipelines and lead to unnecessary investigations for errors even though there are none on our side.
- Calling the external service each time slows down test running. Of course, it takes longer to trigger some HTTP call instead of simply mocking some method, returning some plain value.
- Calling a real service might introduce certain states which will only work in the first run, but might fail in the second one. Imagine the creation of a resource with some identifier. The second time you call it it will throw an exception that the resource already exists.
- Unit Tests should only test the unit you are working on. You don’t want to test another service with it. You should expect the creators of the external service to test their code.
The file structure of our example:
/
- externalService.js
- index.js
- index.spec.js
- package.json
- package-lock.json
Of course, before we can run the Jest tests make sure that you have installed it before via npm i --save-dev jest
.
Our example code:
//------------------------------------------------
// index.js
//-----------------------------------------------
// Import method to call the external service
const { callExternalService } = require("./externalService");
// Method to test
const main = () => {
// We only call the external service and return its response
return callExternalService();
};
module.exports = { main };
//------------------------------------------------
// externalService.js
//------------------------------------------------
// Some external call to a service, returning some result
const callExternalService = () => {
// Do some logic/API calls here...
return true;
};
module.exports = { callExternalService };
//------------------------------------------------
// index.spec.js
//------------------------------------------------
const { main } = require("./index");
// Import the external call we want to
// return a custom value for
const { callExternalService } = require("./externalService");
// Mock the whole external services module
jest.mock("./externalService");
describe("index", () => {
beforeEach(() => {
// Resets the overwritten return values via
// "callExternalService.mockReturnValue" in the test cases
jest.resetAllMocks();
});
it("Should test success case", () => {
// We test the case where the external service
// returns "true"
callExternalService.mockReturnValue(true);
// Call the main() method and expect that it
// return "true" after processing the mocked response
// of our external service call
const result = main();
expect(result).toBe(true);
});
it("Should test failure case", () => {
// We test the case where the external service
// returns "false"
callExternalService.mockReturnValue(false);
// Call the main() method and expect that it
// return "false" after processing the mocked response
// of our external service call
const result = main();
expect(result).toBe(false);
});
});
In the above example, you can see that we test our main()
method for two different responses from our external service. Since we only return true
and false
here we e.g. could expect that the external service is a simple doesResourceExists()
endpoint returning some boolean value to indicate if a resource might exist.
After we run our test cases ( npm run test
), everything is green:
Result after running the “npm run test” command in your console
Of course, the above example is purely for demonstrating purposes.
Conclusion
- You should never ever make external calls when doing unit testing, due to several reasons mentioned at the beginning. It's not Unit Testing then anymore anyway.
- Jest makes it super easy to mock constants/methods imported from other modules/files with just a few LoC.
Thanks for taking the time to read my article.