Reuse Jest module mocks across tests - jestjs

I want to mock a module for a test. Everything works, but I have to copy/paste the same code into every test file. How can I make this more maintainable?
(This is using Babel and TypeScript in a Next.js project.)
The production code looks like this:
import { useRouter } from 'next/router';
// Then call the hook in a React component:
const router = useRouter();
The test code:
// I need to access these mocks in the tests
const mockRouterPush = jest.fn();
const mockRouterBack = jest.fn();
jest.mock('next/router', () => ({
useRouter: () => ({
push: mockRouterPush,
query: { segment: 'seg1' },
back: mockRouterBack,
}),
}));
This works fine. The module and its hook are mocked for the test file, and I can refer to the mocks in the tests. The issue is that it's difficult to maintain across multiple test files.
Calls to jest.mock() are hoisted to the top of the file, and can only refer to variables that start with the word 'mock'.
With other mocks I've used require within the module factory instead of import; this works and reduces boilerplate, but (a) makes it more difficult for the tests to refer to the mocks and (b) my IDE (VSCode) won't auto-update require paths like it does with imports when I move files. For example:
jest.mock('react-i18next', () => {
// Sometimes the path is '../../../testhelpers' etc.
const { mockUseTranslation } = require('./testhelpers');
return {
useTranslation: mockUseTranslation,
};
});
I've tried doMock and createMockFromModule without success.
How do other folks deal with this issue?

Maybe using the __mocks__ directory can help you.
from the docs:
Manual mocks are defined by writing a module in a mocks/ subdirectory immediately adjacent to the module. For example, to mock a module called user in the models directory, create a file called user.js and put it in the models/mocks directory.
You can also mock modules from the node_modules directory.
Manual Mocks

Related

How do I import a rust WASM module in gatsby js?

I'm trying to use my rust module from the rust webassembly book in my gatsby project. When I try to import the module like so:
import { <rust-struct> } from 'rust_wasm_npm_package';
I get the following error:
The module seem to be a WebAssembly module, but module is not flagged as WebAssembly module for
webpack.
BREAKING CHANGE: Since webpack 5 WebAssembly is not enabled by default and flagged as experimental
feature.
You need to enable one of the WebAssembly experiments via 'experiments.asyncWebAssembly: true' (based
on async modules) or 'experiments.syncWebAssembly: true' (like webpack 4, deprecated).
For files that transpile to WebAssembly, make sure to set the module type in the 'module.rules'
section of the config (e. g. 'type: "webassembly/async"').
(Source code omitted for this binary file)
I'm unable to add the experiments option to the gatsby config file, so I'm not sure what is the best way to import a wasm-pack rust module into gatsby.
I was able to get this working by adding a gatsby-node.js file with the following code:
exports.onCreateWebpackConfig = ({ actions }) => {
actions.setWebpackConfig({
experiments: {
syncWebAssembly: true,
},
});
};
I was then able to import the web assembly asynchronously. Not sure why I did not need to use asyncWebassembly: true instead, but it works!
// The reason for this useless concatenation
// is to get rid of a really specific issue
// with Webpack and WASM modules being imported
// all in one line.
/*eslint no-useless-concat: "off"*/
const module = await import("path/" + "toJSFile.js");
const memModule = await import("path/" + "toWasmModule.wasm");
const memory = memModule.memory;
setMem(memory);

mock functions don't work in .test files after adding setupFilesAfterEnv

After adding setupFilesAfterEnv in the jest.config.js like that:
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
setupFilesAfterEnv: ["./test/setupAfterEnv.ts"]
}
the mock functions don't work anymore in the .test.ts files but works in the file that was written inside the setupFilesAfterEnv like ./test/setupAfterEnv.ts
mock function example(the .test.ts file and setupAfterEnv.ts in the same director):
jest.mock("../src/service/sendEmail.ts", () => ({
send: () => {
return "MOCK sendEmail sent"
}
}));
I don't get any errors, and the other function that should be mocked runs. When I write wrong path in the mock function, I get the error can't find module. So it looks it find the function that should be mocked but doesn't run the mock function and run the other function instead
And if I commented the setupFilesAfterEnv config in jest.config.js it works again.
Documentation states
Warning: Importing a module in a setup file
(as specified by setupTestFrameworkScriptFile)
will prevent mocking for the module in question,
as well as all the modules that it imports.
If you have any imports/requires in your ./test/setupAfterEnv.ts, they likely imported other files, and anything chain imported in that process will not be able to be mock('../somemodule'); within tests.
I'm not sure why this restriction is in place; if you call the mock('../somemodule') within the setupAfterEnv.ts file itself, it will be mocked later within your tests. But I consider that bad, as only some tests may want to mock some modules, and others expect them not to be mocked. A pity. In my case, I wanted to import a typeorm library and connect to the database. But the class that creates this connection imports all our entities and all the entity classes import other classes which import other classes etc etc... so nothing becomes mockable. Simply unusable because of this restriction.

Jest Mock Globally From a Node Module

I am writing a series of Node modules that require a bunch of common Jest mocks to be setup before I can write unit tests.
I am trying to refactor the unit test setup into a separate module so I can avoid rewriting the setup each time.
The problem is that when I import the following code as a module it no longer mocks the other libraries, while it works just fine when the mocks are set up in a utility file.
Working Code:
jest.mock('#actions/core')
jest.mock('#actions/github')
const { GitHub, context} = require('#actions/github')
const core = require('#actions/core')
GitHub.mockImplementation(() => {
return {
{
repos: {
getContents: jest.fn()
}
}
}
}
module.exports = { core, GitHub, context }
I keep this in a utils.js file next to my test files and import it like const { core, GitHub, context } = require('./utils.js') and everything mocks as I expect. I can run expect().toHaveBeenCalledTimes() and get the numbers I expect.
The problem appears when I move utils.js to another module and require it.
I know that at bottom of the jest documentation it says "...Another file that imports the module will get the original implementation even if it runs after the test file that mocks the module." But I am seeing this work inconsistently with the external file.
Anyone know how to get mocking setup in external modules?

TestCafe: import tests from another file into the current fixture

I have a file tests.js that contains some test(...) definitions. I want to reuse these tests across multiple fixtures, preferably without making any modifications to the original code.
So I wrote a main.js that defines a fixture and imports tests.js, thereby "assembling" a test suite. (In case that works, I could write different driver files with different fixtures, importing the same tests.js from within each.)
However, I'm getting a test is not defined error when trying to execute main.js:
C:\Windows\Temp\dummy>testcafe chrome main.js --debug-on-fail
ERROR Cannot prepare tests due to an error.
ReferenceError: test is not defined
at Object.<anonymous> (C:\Windows\Temp\dummy\tests.js:1:1)
at Object.<anonymous> (C:\Windows\Temp\dummy\main.js:7:1)
Type "testcafe -h" for help.
Minimal sample:
// tests.js
test('wait', async t => {
await t.wait(1);
});
// main.js
fixture `here goes the name`
.page("http://localhost:3000")
.beforeEach(async t => {
// do stuff
});
import "./tests";
/*
trick testcafe to scan the file;
based on https://github.com/DevExpress/testcafe/issues/2889#issuecomment-423859785
test();
*/
I already tried:
removing the block comment hack (test();) - which gives ERROR No tests to run. Either the test files contain no tests or the filter function is too restrictive.
moving the tests.js import to the top - still gives test is not defined
importing testcafe from within main.js and tests.js - same error
Is there a way to make the test function "visible" to other files imported by the testcafe entrypoint file? Or will I actually need to modify my tests.js file in order to get this working? Maybe by adding the test definitions into a method, and invoking it from within main.js - as in the original code sample of this issue?
TestCafe doesn't allow calling fixture and test functions outside the test scope. You can wrap your tests from the tests.js file in a function and call this function in the main.js file:
// tests.js
export default function () {
test('Test 1', () => {});
test('Test 2', () => {});
test('Test 3', () => {});
}
// main.js
import defineTests from './tests';
defineTests();
See also: Organize Tests
Try to add the option --disable-test-syntax-validation on the TestCafe command-line
(works only in latest TestCafe version).

How can I avoid always having to import my own code in Typescript?

So I was recently hacking on a large Typescript project (https://github.com/BabylonJS/Babylon.js) and I noticed that they don't ever have to import anything, they just use their namespace and the rest is (seemingly) magic.
It got me thinking that I would like to use something similar for myself, so I started a simple typescript project to try it out.
tsconfig.json
{
"compilerOptions": {
"baseUrl": "src",
"outFile": "server.js"
}
}
src/main.ts
module Test {
console.log('Main started')
const server:Server = new Server()
}
src/Server.ts
// import * as http from 'http'
module Test {
export class Server {
constructor() {
console.log('Server initialized')
}
}
}
If I build this Typescript project then I get output like the following:
// import * as http from 'http'
var Test;
(function (Test) {
var Server = /** #class */ (function () {
function Server() {
console.log('Server initialized');
}
return Server;
}());
Test.Server = Server;
})(Test || (Test = {}));
var Test;
(function (Test) {
console.log('Main started');
var server = new Test.Server();
})(Test || (Test = {}));
So far, so good. The trouble is that I want to take advantage of some external modules, in this case namely the http module, so I uncomment the import line above and now Typescript reports:
src/server/Server.ts(1,1): error TS6131: Cannot compile modules using option 'outFile' unless the '--module' flag is 'amd' or 'system'.
Node uses the commonjs module system, so obviously setting either of those flags isn't going to help me much. I have none the less tried them as well as various other combinations of flags, but to no avail. I noticed that BabylonJS doesn't really use external imports like that, opting instead to declare them as external and provide them globally at execution time as script tags. Is there maybe an analogue to that for Node?
You can't have these two things at the same time, namely
How can I avoid always having to import my own code in Typescript?
and
I want to take advantage of some external modules
You can avoid imports only by not using external modules, and the result will be one giant script file that can use external dependencies only as globals created by scripts loaded via script tag, as you already noticed.
The language does not allow you to use external modules when you do this. If you have an import of external module at the top level, your file becomes a module and there is no way it could use code from your other files without importing them. And having import of external module inside a namespace is not allowed AFAIK.
That said, I don't think your question - "How can I avoid always having to import my own code in Typescript?" - has a valid premise. CommonJS module system is the solution for preventing large projects from becoming unmaintainable mess. It does not matter if some part of a project is your own code or some external dependency - if it's a separate part with well-defined interface, it should be packaged and consumed as a module.
The solution that worked for me is this:
Server.ts
declare var http: any;
namespace Test {
export class Server {
constructor() {
console.log('Server initialized')
const server = http.createServer()
server.listen()
}
}
}
Then I simply provide http at runtime, for example by prepending a var http = require('http') to the output. Thanks to artem for a nudge in the right direction.

Resources