Exploring Mocking Internal Dependencies
Prior to mocking internal dependencies, it is essential to define the purpose behind it; especially considering Jest's limitations in this regard. This article aims to present some potential scenarios and examples that can shed light on this topic.
Let's delve into a hypothetical scenario where there exists a function named testFunction()
that returns the previously mentioned storageConnectionString
.
1. Targeting Specific Functions within Test Scope
// key.spec.js
const keyvault_emulator = require('../keyvault-emulator');
js.mock('../keyvault-emulator', () => ({
// everything remains untouched except for testFunction
...jest.requireActual('../keyvault-emulator'),
// mock implementation of testFunction
testFunction: jest.fn(() => 'mocked')
})
// ✅ Success
expect(keyvault_emulator.testFunction()).toEqual('mocked')
// ❌ Failure!
expect(keyvault_emulator.otherFunctionUsingStorageConnectionString())
.toEqual('mocked')
2. Replacing all Module Content
In Jest, the capability lies in substituting an entire function or module rather than re-evaluating code snippets. In cases like this, decoupling dependencies such as config.js
and keyvault-emulator.js
from the source proves to be beneficial for testing purposes.
2-1. Implementing Hard Decoupling directly in the Source
// keyvault-emulator.js
class KeyValueEmulator {
constructor(externalConfig) {
this.externalConfig = externalConfig;
}
testFunction() {
// process involving this.externalConfig
return this.externalConfig;
}
}
// key.spec.js
const mockedExternalConfig = { storageConnectionConfig: 'mocked' }
const keyValueEmulator = new KeyValueEmulator(mockedExternalConfig);
// ✅ Successful execution
expect(keyValueEmulator.testFunction()).toEqual('mocked')
2-2. Employing Soft Decoupling with Jest's Internal Module Mocking
Refer to working codes on GitHub
// key.spec.js
import config from "./config";
jest.mock("./config", () => ({ default: { storageConnectionString: "mocked" } }));
import { storageConnectionString, testFunction } from "./index";
describe("config mocking", () => {
it("value is mocked", () => {
// ✅ Works as expected
expect(config.storageConnectionString).toEqual("mocked");
expect(testFunction()).toEqual("mocked");
});
});
3. Mocking a Single Variable
As elaborated in case 2, mocking a single variable poses challenges with Jest's current functionalities. Hence, precise clarification and adjustments are needed when attempting such operations.
// key.spec.js
const keyvault_emulator = require('../keyvault-emulator');
const spy = jest.spyOn(keyvault_emulator, 'storageConnectionString');
spy.mockReturnValue('mocked');
// ✅ Potential success...however
expect(keyvault_emulator.storageConnectionString).toEqual('mocked')
// ❌ Unsuccessful outcome
expect(keyvault_emulator.testFunction()).toEqual('mocked')
Determining the Optimal Approach
Is this method considered effective? What strategy works best for achieving desired outcomes?
Similar to numerous developer dilemmas, the most suitable approach varies depending on the specific context. While opting for hard decoupling, as highlighted in 2-1, serves well in many scenarios, it may not always be the perfect solution. Select the methodology that aligns seamlessly with your unique circumstances.