I am trying to mock the useNavigate hook using Jest. I was able to do it at the top level of my test suit (I also have a custom render with BrowserRouter as a wrapper)
const mockedUsedNavigate = jest.fn();
jest.mock('react-router-dom', () => ({
...(jest.requireActual('react-router-dom') as any),
useNavigate: () => mockedUsedNavigate,
}));
...
expect(mockedUsedNavigate).toHaveBeenCalledWith('myurl/user-1');
My issue is that I want to have this mock available inside the __mocks__ directory so I can use it in other tests as well instead if having to write it again and again. I wasn't able to do it.
Is it possible?
You should be able create a src.setupTests.js file in your project and declare the mock there.
See Initializing Test Environment
src/setupTests.js
export const navigateMock = jest.fn(); // <-- exported for test assertions
jest.mock('react-router-dom', () => ({
...(jest.requireActual('react-router-dom') as any),
useNavigate: () => navigateMock,
}));
You can then import the exported navigateMock function in test files.
Related
I was just trying to do a preliminary test of rendering a component that is redirecting the user on the basis of the login status and thus using router from next/router in componentDidMount but getting the following error:
No router instance found.
You should only use "next/router" inside the client side of your app.
It appears to me that from client side it means using the router or Link has to be used inside of the render method as that is what makes the DOM and other methods like lifecycle, hooks, and server-side doesn't so in those cases it would throw an error.
I know that testing the entire component beats the purpose of unit testing but I wanted to do this anyway. Therefore, I followed this discussion and it appears that the router has to be mocked in order to be used by the React-Testing-Library but none of the solutions work for me.
Here is the code that I tried:
describe('Home Page', () => {
it('renders without crashing', async () => {
render(<Home />)
})
})
a solution like:
import { useRouter } from 'next/router'
...
jest.mock('next/router', () => ({
useRouter: jest.fn(),
}))
...
;(useRouter as jest.Mock).mockImplementation(() => ({
pathname: '/',
push: mockPush,
}))
will work.
the error you're encountering will be triggered if you do not mock useRouter in the initial mock (i.e.:
...
jest.mock('next/router')
...
Mocked the router using the next-router-mock library. Just pasted jest.mock('next/router', () => require('next-router-mock')); before the test and the tests are passing.
jest.mock('next/router', () => require('next-router-mock'));
describe('Home Page', () => {
it('renders without crashing', async () => {
render(<Home />)
})
})
Although, the test is passing but getting this warning that is not making any sense to me.
› 1 snapshot obsolete.
• Home Page renders without crashing 1
Snapshot Summary
› 1 snapshot obsolete from 1 test suite. To remove it, press `u`.
↳ tests/homePage.test.js
• Home Page renders without crashing 1
Assuming that there is a better way to mock the router.
I'm writing e2e tests using Jasmine and Protractor with AngularJS. I have a parent describe which describes the page, and some setup call in beforeAll that goes to the correct page for the following tests. I've broken these tests up into multiple describes per feature. Here is an example:
describe('Page Detail', () => {
beforeAll(() => {
utils.doSomething();
})
describe('Location Section'), () => ...
describe('Information Section', () => ...
The problem I'm having is that there are a lot of features within this module, and the tests are starting to push 300-400 lines of code. I would ideally like to put the nested describes in seperate files and import them. I've done something like this:
const describeLocation = require('./folder/location'),
describeInformation = require('./folder/information');
describe('Page Detail', () => {
beforeAll(() => {
utils.doSomething();
})
describeLocation();
describeInformation();
In the other files I'm simply exporting an anonymous function and including the nested describe's code. This works, but unfortunately the tests don't have the jasmine context (can't access anything in beforeAll).
I'm curious if there is a standard or better way to accomplish this?
//export.js
describe(...
)
//Import or your main specs file
describe('',()=>{
require('./export');
})
Don't export in a form of a method just write your spec and import it using require in the main describe.
I've had trouble spying on exported function in a NodeJS (v9.6.1) app using Jasmine.
The app is written in typescript, transpiled with tsc in a dist folder to be executed as javascript.
App
I have a Foo utils file (foo.utils.ts) which exports functions:
import {readFile} from "fs";
export function getData(filePath: string){
const data = readFile(filePath)
// various checks happens here.
return data;
}
And a Bar class in a bar.ts file:
import {getData} from "../utils/foo.utils
export class Bar {
public do(){
// ... does stuff
const data = getData(filePath);
// etc.
}
}
Test
Now I'm trying to spyOn the exported getData method to check if it've been called and to return a mocked value. I don't want to read file in my unit test (and even to use the real getData method at all)
The bar.spec.ts file for testing:
describe("Bar", ()=>{
let bar: Bar;
beforeEach(function() {
bar = new Bar();
})
it(" should do stuff", ()=>{
const spy = spyOn(???, "getData").and.returnValue([]);
bar.do();
expect(spy).toHaveBeenCalled();
})
});
Problems
As it is a NodeJS app, I've been trying to use global as the object to spy on, but I get the error:
Error: : getAttachementData() method does not exist
I've also tried to add
import * as fooutils from ".../src/utils/foo.utils
And spy on fooutils but I still goes through the exported function (and crashes when it tries to read the file...)
Now I'm kinda lost. From what I've found it's not really possible to mock an exported function (even though they should be added to the global object).
I thought about refactoring the utils file to create an utils class which exports static method and spying on them.
Questions
Am I doing something wrong ?
Is it possible to spy (and replace) exported function ?
Would using static method in class (rather than export function) work ?
Any other way to that ?
You can wrap getData in an object { getData } and write your unit test as follows.
import { getData } from "../utils/foo.utils";
...
it(" should do stuff", () => {
const spy = spyOn({ getData }, "getData").and.returnValue([]);
bar.do();
expect(spy).toHaveBeenCalled();
});
I have a Node.js project that I'm testing using Jest. I have several test files that have the same setup requirement. Previously, all these tests were in one file, so I just had a beforeAll(...) that performed the common setup. Now, with the tests split into multiple files, it seems like I have to copy/paste that beforeAll(...) code into each of the files. That seems inelegant - is there a better way to do this, ideally where I can just write my beforeAll(...)/setup logic once, and "require" it from multiple test files? Note that there are other tests in my test suite that don't require this setup functionality, so I don't want to make all my tests run this setup (just a particular subset of test files).
If you're using Jest >=20, you might want to look into creating a custom jest-environment for the tests that require this common setup. This would be a module that extends either jest-environment-node or jest-environment-jsdom, and implements async setup(), async teardown(), and async runScript() to do this setup work.
You can then add a #jest-environment my-custom-env directive to those files that require this setup.
See the Jest config docs for testEnvironment for details on how to set this up; there's a simple example there.
I am using a simple "test hooks" pattern for this:
// This function wraps beforeAll and afterAll into a single RAII-like call.
// That makes the describe code further down easier to read and makes
// sure you don't forget the afterAll part. Can easily be shared between tests.
function useFakeServer() {
let server;
beforeAll(() => server = sinon.fakeServer.create());
afterAll(() => server.restore());
return () => server;
}
describe('Some scenario', () => {
const getServer = useFakeServer();
it('accesses the server', () => {
const server = getServer();
// Test as you normally would..
expect(server.requests[0]. /* ... */);
});
});
If you need a script to run before all your test files, you can use globalSetup
This option allows the use of a custom global setup module which exports an async function that is triggered once before all test suites.
in your jest.config.js
//jest.config.js
module.exports = {
...
testTimeout: 20000,
globalSetup: "./setup.js"
};
then create a file named setup.js
// setup.js
module.exports = async () => {
console.log("I'll be called first before any test cases run");
//add in what you need to do here
};
Docs
You can move your beforeAll logic into one file and reference it in jest.config.js setupFilesAfterEnv section:
module.exports = {
...
setupFilesAfterEnv: ['<rootDir>/testHelper.ts'],
...
}
https://jestjs.io/docs/en/configuration#setupfilesafterenv-array
Create a function somewhere like so:
export function setupBeforeAndAfter(putParamsHereIfYouHaveAny) {
beforeAll(() => shared-before-all-code);
afterAll(() => shared-after-all-code);
beforeEach(() => shared-before-each-code);
afterEach(() => shared-after-each-code);
}
Then just call it wherever you would otherwise have manually written these functions:
describe('My test', () => {
setupBeforeAndAfter(putParamsHereIfYouHaveAny)
it('is amazing', () => {
// Stuff in setupBeforeAndAfter() will run before/after this test as appropriate
})
})
For some reason Jest is not picking up my manual mock implementation. The structure is the following. Am I missing something here? It is for sure hoisting the .mock call, I've tested it with the mock call above imports and still no dice.
Appreciate any help.
--
// "#/store/modules/__tests__/data"
import {fetchData} from "#/store/modules/data"
import {fetchByids} from "#/api/fetch";
jest.mock("#/api/fetch");
...
[ fetchByIds is considered the original still ? ]
--
// "#/store/modules/data"
import {fetchByIds} from "#/api/fetch";
...
export const fetchData = (context, payload) => {
fetchByIds(payload.id);
}
--
// "#/api/fetch"
export const fetchByIds = (id) => fetch(`url${id}`).then(response=>response.json())
--
// "#/api/__mocks__/fetch"
export const fetchByIds = jest.fn().mockImplementation(id => Promise.resolve({id}))
--
What's interesting is if I add the following to "#/store/modules/__tests__/data" it picks up the mock implementation. So maybe it's an issue with name mapping?
jest.mock("#/api/fetch", () => ({
fetchByIds: jest.fn().mockImplementation(id => Promise.resolve({id}))
}));