The goal of this babel plugin is to make it easier mock things when writing unit tests without having to change how you'd normally code. In particular it allows you to mock top-level functions and variables from any source module even if it hasn't been exported.
foo.js
const msg = "foo";
export const foo = () => msg;
foobar.js
import {foo} from "./foo.js";
const msg = "bar";
const bar = () => msg;
export const foobar = () => {
return foo() + bar();
};
example.test.js
const mockValue = (obj, prop, value) => {
jest.spyOn(obj, prop, "get").mockReturnValue(value);
};
test("mocking private variable", () => {
mockValue(FooBar, "msg", "baz");
expect(FooBar.foobar()).toEqual("foobaz");
});
test("mocking private function", () => {
jest.spyOn(FooBar, "bar").mockReturnValue("baz")
expect(FooBar.foobar()).toEqual("foobaz");
});
test("mocking private variable in dependency", () => {
mockValue(Foo, "msg", "qux");
expect(FooBar.foobar()).toEqual("quxbar");
});
test("mocking function from dependency", () => {
jest.spyOn(Foo, "foo").mockReturnValue("qux")
expect(FooBar.foobar()).toEqual("quxbar");
});
See the example.test.js for the full test suite.
All top-level declarations in all non-test modules to be named exports
using commonjs' exports
object and updates all references to each
variable accordingly.
input.js
const msg = "foo";
const foo = () => msg;
export const foobar = () => `${foo()}bar`;
output.js
const msg = "foo",
Object.defineProperty(exports, "msg", {
enumerable: true,
configurable: true,
get: () => msg,
});
let foo = () => exports.msg;
Object.defineProperty(exports, "foo", {
enumerable: true,
configurable: true,
get: () => foo,
set: (newValue) => foo = newValue,
});
let foobar = () => `${exports.foo()}bar`;
Object.defineProperty(exports, "foobar", {
enumerable: true,
configurable: true,
get: () => foobar,
set: (newValue) => foobar = newValue,
});
Object.defineProperty(exports, "__esModule", {
value: true,
});
The reason for setting exports.__esModule = true
at the end is that
other modules importing this module may end up using _interopRequireDefault
in the code generated by babel. This helper has the following defintion:
function _interopRequireDefault(obj) {
return obj && obj.__esModule
? obj
: { default: obj };
}
const Foo = _interopRequireDefault(require("./output.js"));
In order have consistent operation regardless of whether the helper is
being used or not, we need to set __esModule = true
so that we always
returns whatever is returned by calls to require
.
In order for coverage to work, please set coverageProvider: "v8"
in
your jest configuration. Without this, some tests will fail because of
how the traditional code coverage modifies your code to instrument it.
require
-ing dependencies- re-exporting things
- renaming things in exports, e.g.
export { foo as bar, baz as default }
- createReactClass classes
jest caches compiled files so often times you'll need to clear the cache before your changes will show up. This can be done by running:
yarn test --clearCache