I’d like to introduce the new .stub
feature in Maineffectjs. Here is a simple file called sum.js
that has a two
function ..
import { logger } from './logger';
export const two = () => {
logger('this').file('is').debug('awesome');
return 2;
};
A test file written using Jest could look like this ..
import { logger } from "./logger";
import { two } from "./sum";
var mockLogger = {
file: jest.fn().mockReturnThis(),
debug: jest.fn().mockReturnThis(),
};
jest.mock("./logger", () => {
return {
__esModule: true,
logger: jest.fn(() => {
return mockLogger
}),
};
});
describe("two", () => {
it("should call logger, file, and debug methods with the correct arguments", () => {
const result = two();
expect(logger).toHaveBeenCalledWith("this");
expect(mockLogger.file).toHaveBeenCalledWith("is");
expect(mockLogger.debug).toHaveBeenCalledWith("awesome");
});
});
In the above example, we carefully re-construct the structure of logger
in the mockLogger.
Let’s look at those lines of code again.
var mockLogger = {
file: jest.fn().mockReturnThis(),
debug: jest.fn().mockReturnThis(),
};
jest.mock("./logger", () => {
return {
__esModule: true,
logger: jest.fn(() => {
return mockLogger
}),
};
});
I find this awful. There is so much magic going on here a developer has to understand. The longer the chain of functions, the more painful it gets. Wouldn’t it be great if these mocks were automatically created for you?
Now the new way with Maineffectjs.
import { parseFn, Stubs } from "maineffectjs";
describe('two', () => {
const parsed = parseFn(require.resolve("./sum"));
const stubs = Stubs(jest.fn);
it('should call logger, file, and debug methods with the correct arguments', () => {
parsed
.find("two")
.stub("logger().file().debug()", stubs.createStub)
.callWith();
expect(stubs.getStubs().logger).toBeCalledWith("this");
expect(stubs.getStubs().file).toBeCalledWith("is");
expect(stubs.getStubs().debug).toBeCalledWith("awesome");
});
});
With the new stub
function that takes a key like “logger().info().debug()”
Maineffectjs knows that these are chained function calls and have to be stubbed automatically.
.stub("logger().info().debug()", stubs.createStub)
This also works for a mix of Objects and Functions. Below is an example
.stub("logger.info().debug()", stubs.createStub)
Maineffectjs understands the hint given. It sees we want to stub out a function called logger
that should return a mock value with the info
key. The value for that should be a mocked info
function and so on ..
We can also bring-our-own-stub implementation ( Jest or Sinon )
const stubs = Stubs(jest.fn); // Jest
const stubs = Stubs(sinon.stub); // Sinon
The actual stubs get stored as inside the stub creator object. You can use your favorite assertions libraries on them. Say you stubbed log()
the key under which the stub is saved will be log.
expect(stubs.getStubs().logger).toBeCalledWith("this");
expect(stubs.getStubs().file).toBeCalledWith("is");
expect(stubs.getStubs().debug).toBeCalledWith("awesome");
Let me know what you guys think in the comments. Happy Testing!