React jest test with enzyme error - reactjs

Trying to test my react-native app with jest and enzyme as follows.
import 'react-native';
import React from 'react';
import renderer from 'react-test-renderer';
import { shallow } from 'enzyme';
import { SignUp } from '../../src/pages';
describe('Testing SignUp component', () => {
it('renders as expected', () => {
const wrapper = shallow(
<SignUp />
);
expect(wrapper).toMatchSnapshot();
});
});
on running npm test I get error Cannot find module 'enzyme/build/ShallowTraversal' from 'shallow.js'
test suite failed to run.
Please note that test done minus enzyme runs properly
test('SignUp Page renders correctly', () => {
const tree = renderer.create(<SignUp />).toJSON();
expect(tree).toMatchSnapshot();
});
What could be wrong?

Just solved the problem by first installing enzyme-to-json; npm install --save-dev enzyme-to-json
imported toJson inside my test_file.js
import 'react-native';
import React from 'react';
import renderer from 'react-test-renderer';
import { shallow } from 'enzyme';
import toJson from 'enzyme-to-json'; //added this line
changed the test to this
describe('Testing SignUp component', () => {
it('renders as expected', () => {
const wrapper = shallow(
<SignUp />
);
expect(toJson(wrapper)).toMatchSnapshot(); //edited this line
});
});
changed snapShotSerializers in package.json
"preset": "react-native",
"collectCoverage": true,
"collectCoverageFrom": [
"**/src/**.{js,jsx}"
],
"snapshotSerializers": ["enzyme-to-json/serializer"] //added this line
Now my tests are running correctly.

Related

Mocking react-native Appearance with Jest [duplicate]

I'm trying to mock only one function in imported React module, keep the rest of the module unmocked and do this at top level for all tests.
I'm using fresh create-react-app project with a single test to observe the problem.
Steps to reproduce:
create-react-app test
use provided src/App.test.js as the only test file
npm run test
App.test.js
jest.mock('react', () => {
jest.dontMock('react');
const React = require('react');
const lazy = jest.fn();
return {
...React,
lazy
};
});
import * as React from 'react';
const React2 = require('react');
it('should partially mock React module', async () => {
expect(jest.isMockFunction(React.lazy)).toBe(true); // passes
expect(jest.isMockFunction(React2.lazy)).toBe(true); // fails
expect(jest.isMockFunction(require('react').lazy)).toBe(true); // fails
expect(jest.isMockFunction((await import('react')).lazy)).toBe(true); // fails
});
The problem here seems to be jest.dontMock as it prevents require and dynamic import from being mocked, but it remains unclear why it was possible to mock static import this way, as it uses require any way. Here's transpiled file:
"use strict";
jest.mock('react', () => {
jest.dontMock('react');
const React = require('react');
const lazy = jest.fn();
return (0, _objectSpread2.default)({}, React, {
lazy
});
});
var _interopRequireWildcard3 = require("...\\node_modules\\#babel\\runtime/helpers/interopRequireWildcard");
var _interopRequireDefault = require("...\\node_modules\\#babel\\runtime/helpers/interopRequireDefault");
var _interopRequireWildcard2 = _interopRequireDefault(require("...\\node_modules\\#babel\\runtime/helpers/interopRequireWildcard"));
var _objectSpread2 = _interopRequireDefault(require("...\\node_modules\\#babel\\runtime/helpers/objectSpread"));
var React = _interopRequireWildcard3(require("react"));
const React2 = require('react');
...
This may have something to do with create-react-app Jest+Babel setup because I was unable to make jest.dontMock work incorrectly with vanilla Jest and require.
Why is static React import mocked but React2 and the rest aren't? What exactly is going on inside?
How can jest.dontMock current behaviour be fixed to partially mock a module at top level?
Default Imports:
A simple solution would be to mock React.lazy in the setupTest.js:
import React from 'react';
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
jest.spyOn(React.lazy);
Any subsequent require/imports of react will be partially mocked for each test file.
Working example: https://github.com/mattcarlotta/react-lazy-mocked (I don't use the create-react-app, but jest can be set up the same way as I have it)
Installation:
git clone git#github.com:mattcarlotta/react-lazy-mocked.git
cd react-lazy-mocked
yarn install
yarn test
root/__tests__/root.test.js
import React from 'react';
import App from '../index.js';
const React2 = require('react');
describe('App', () => {
const wrapper = mount(<App />);
it('renders without errors', () => {
const homeComponent = wrapper.find('.app');
expect(homeComponent).toHaveLength(1);
});
it('should partially mock React module', async () => {
expect(jest.isMockFunction(await require('react').lazy)).toBe(true); // eslint-disable-line global-require
expect(jest.isMockFunction(React)).toBe(false);
expect(jest.isMockFunction(React.lazy)).toBe(true);
expect(jest.isMockFunction(React2)).toBe(false);
expect(jest.isMockFunction(React2.lazy)).toBe(true);
});
it('should no longer be partially mocked within the test file', () => {
React.lazy.mockRestore();
expect(jest.isMockFunction(React.lazy)).toBe(false);
});
});
pages/Home/__tests__/Home.test.js
import React from 'react';
import Home from '../index.js';
describe('Home', () => {
const wrapper = shallow(<Home />);
it('renders without errors', () => {
const homeComponent = wrapper.find('.app');
expect(homeComponent).toHaveLength(1);
});
it('should partially mock React module', async () => {
expect(jest.isMockFunction(React.lazy)).toBe(true);
});
});
Named Imports:
Working example: https://github.com/mattcarlotta/named-react-lazy-mocked
Installation:
git clone git#github.com:mattcarlotta/named-react-lazy-mocked.git
cd named-react-lazy-mocked
yarn install
yarn test
utils/__mocks__/react.js
jest.mock('react', () => ({
...require.requireActual('react'),
lazy: jest.fn(),
}));
module.exports = require.requireMock('react');
utils/setup/setupTest.js (optionally, you can add the mocked react file as a global jest function so you won't have to write import * as React from 'react' for every test):
import { JSDOM } from 'jsdom';
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
// import React from '../__mocks__/react';
configure({ adapter: new Adapter() });
// global.React = React;
root/__tests__/root.test.js
import * as React from 'react';
import App from '../index.js';
const React2 = require('react');
describe('App', () => {
const wrapper = mount(<App />);
it('renders without errors', () => {
const homeComponent = wrapper.find('.app');
expect(homeComponent).toHaveLength(1);
});
it('should partially mock React module', async () => {
expect(jest.isMockFunction(await require('react').lazy)).toBe(true); // eslint-disable-line global-require
expect(jest.isMockFunction(React)).toBe(false);
expect(jest.isMockFunction(React.lazy)).toBe(true);
expect(jest.isMockFunction(React2)).toBe(false);
expect(jest.isMockFunction(React2.lazy)).toBe(true);
});
});

Jest-dom give the error "TypeError: expect(...).toHaveStyle is not a function"

I'm trying to use jest-dom to test a component's styles, and I'm having the error:
"TypeError: expect(...).toHaveStyle is not a function"
My component is a simple link with styled-components:
import styled from 'styled-components'
export const Link = styled.a`
color: #fff;
`
In my test I'm doing:
describe('Link', () => {
it('should display color on the link', () => {
render(<Link href="/x">Test</Link>)
}
expect(screen.getByRole('link', { name: /test/i })).toHaveStyle({ color: '#fff' })
}
I created a settings file (jest.config.js) with:
module.exports = {
setupFilesAfterEnv: ['<rootDir>/.jest/setup.js'],
}
At the root of the project I created the .jest / setup.js file and imported the js-dom:
import '#testing-library/jest-dom'
From https://testing-library.com/docs/svelte-testing-library/setup/
6.2 Add the following to your Jest configuration in package.json
{
"setupFilesAfterEnv": ["#testing-library/jest-dom/extend-expect"]
}
You could add it to your jest.config.js since you already have that, rather than package.json.
Since #testing-library/dom v5.0.0, there are some breaking changes compare/v4.2.4...v5.0.0
Before v5.0.0, you should use import '#testing-library/jest-dom/extend-expect';
Since v5.0.0, you should use import '#testing-library/jest-dom';
You didn't add the matchers for expect correctly. That's the reason you get the error.
you can try this :
import { toHaveStyle } from '#testing-library/jest-dom'
expect.extend({ toHaveStyle })
it works for me.
Use the following versions in your package:
"dependencies": {
"#types/styled-components": "5.9.1",
"styled-components": "^5.2.0"
},
and import this package into your test file:
import '#testing-library/jest-dom/extend-expect'
I found the answer from this link
To be short:
Run: npm install --save-dev #testing-library/jest-native
Add: import '#testing-library/jest-native/extend-expect' to your test file
Add: import { toHaveStyle } from '#testing-library/jest-native/dist/to-have-style'
Add: expect.extend({toHaveStyle})
Finally, you have the toHaveStyle for your expect
Sample code:
/**
* #format
*/
import 'react-native';
import '#testing-library/jest-native/extend-expect';
import React from 'react';
import App from '../App';
import {render, screen} from '#testing-library/react-native';
import { toHaveStyle } from '#testing-library/jest-native/dist/to-have-style';
expect.extend({toHaveStyle})
it('renders correctly', () => {
render(<App/>);
const text = screen.getByTestId('Sample Text')
expect(text).not.toBeNull();
const button = screen.getByTestId('Sample Button')
expect(button).not.toBeNull();
expect(button).toHaveStyle({
backgroundColor: 'transparent'
})
});
install jest-styled-components
npm i -D install jest-styled-components
then use .toHaveStyleRule
Example:
import React from 'react';
import { render } from 'react-testing-library';
import Button from '../Button';
import 'jest-styled-components';
describe('<Button />', () => {
it('should render text', () => {
const { getByText } = render(<Button text="Button" />);
expect(getByText('Button')).not.toBeNull();
});
it('should have correct style with background color', () => {
const { getByText } = render(<Button text="Button" color="#fff" />);
expect(getByText('Button')).toHaveStyleRule('background', '#fff');
});
});

spyOn not working while Jest 24.1 is installed

In my package.json I have jest 24.1.0 yet my test tell me
"TypeError: _jest.default.spyOn is not a function"
The Jest docs say spyOn is a method I can use but somehow it is not available to me. What am I doing wrong?
https://jestjs.io/docs/en/jest-object#jestspyonobject-methodname
here is my test...
import React from 'react';
import jest from 'jest';
import Enzyme, { shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({adapter: new Adapter()});
import NavLink from '../Tabs/NavLink';
describe('NavLink', () => {
it('handles onClick prop', () => {
const onClick = jest.fn();
const e = jest.spyOn('e', ['preventDefault']);
const wrapper = shallow(
<NavLink onClick={onClick} />
);
wrapper.find('a').simulate('click', e);
expect(onClick).toHaveBeenCalled();
expect(e.preventDefault).not.toHaveBeenCalled();
});
}
Just remove this line:
import jest from 'jest';
Jest finds and runs your tests, so jest already exists in the scope of your test by the time it is running. There is no need to import it and doing so is causing the error you are seeing.

react unit testing - Enzmye shallow

I am trying to setup unit testing in React with Enzyme. When i run the command "npm-test" the test fails.
The console terminal indicates that it failed because of shallow().
I have installed enzyme using this command npm install --save enzyme enzyme-adapter-react-16 react-test-renderer. Do anyone know how to solve this issue?
Below is the component
import React from 'react';
class Login extends Component {
render() {
return <div><input
onChange={(event) => {
this.setState({input: event.target.value})}}
type="text" /></div>;
}
}
export default Login;
This is the unit test i have written for the Component.
import React from 'react';
import { configure, shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import Login from '../../../src/components/authentication/Login';
import Enzyme from 'enzyme';
configure({adapter: new Adapter()});
it("should create an entry in component state with the event value", () => {
// given
const component = shallow(<Login/>);
const form = component.find('input');
// when
form.props().onChange({target: {
name: 'myName',
value: 'myValue'
}});
// then
expect(component.state('input')).toEqual('myValue');
});
Thanks for the help.
Hi perhaps you miss the conventions:
you have to put your test files inside a __tests__ directory and tests file should be named: YourComponent.test.js
Than wrap your test within a test suite:
describe('Your test suite', () => {
test('it should do something', () => {
// the content of your test
});
});

Test a create-react-app index.js file

I would like to have 100% coverage on my project.
In order to do so, I need to test my index.js file which is very basic :
index.js
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(<App/>, document.getElementById('root'));
I can't find how to test this.
When creating a function such as :
index.js
const index = (div) => {
ReactDOM.render(<App />, div || document.getElementById('root'));
};
and then testing it :
index.test.js
it('renders without crashing', () => {
const div = document.createElement('div');
index(div);
});
I get an error when importing index:
Invariant Violation: _registerComponent(...): Target container is not a DOM element.
PS:
Note that I already have the following test, working perfectly :
App.test.jsx
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App/>, div);
});
If 100% coverage on your project is your goal and the code in your index.js file is trivial, then it might be a good option to exclude the file from the coverage report, as Andreas Köberle points out in his answer.
Create-react-app currently only supports these four keys in the Jest configuration (source):
collectCoverageFrom
coverageReporters
coverageThreshold
snapshotSerializers
This is why
coveragePathIgnorePatterns": ["src/index.js"]
won't work.
Add following code to the most outer scope of your package.json file:
"jest": {
"collectCoverageFrom": [
"src/**/*.{js,jsx}",
"!src/index.js"
]
}
In the image below you see the output of a test run with this code added to the package.json of the initial app created with create-react-app v1.4.3. Note that the index.js file doesn't show up in the report anymore and also doesn't affect the coverage percentage.
This is how I've tested index.js
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(<App />, document.getElementById("root"));
index.test.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
jest.mock("react-dom", () => ({ render: jest.fn() }));
describe("Application root", () => {
it("should render without crashing", () => {
const div = document.createElement("div");
div.id = "root";
document.body.appendChild(div);
require("./index.js");
expect(ReactDOM.render).toHaveBeenCalledWith(<App />, div);
});
});
The main question is what you want to test there. If you want to test that your code works correct, write a unit test that spies on ReactDOM.render and mocks document.getElementById('root'). Cause this is all your code does, calling ReactDOM.render with our App component and a specific div.
import ReactDOM from 'react-dom';
...
jest.mock('react-dom', ()=> ({render: jest.fn()}))
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App/>, div);
global.document.getElementById = (id) => id ==='root' && div
expect(ReactDOM.render).toHaveBeenCalledWith(...)
});
If you want test that the app really starts in your page, you should write integration test with Selenium or Nightwatch.js
To just get 100% coverage you can also ignore this file by adding it to the coveragePathIgnorePatterns in your jest settings
I found an article online that explains this way to write the test...
// index.test.js
import Index from './index.js';
it('renders without crashing', () => {
expect(JSON.stringify(
Object.assign({}, Index, { _reactInternalInstance: 'censored' })
)).toMatchSnapshot();
});
Now change the index.js file accordingly:
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
export default ReactDOM.render(
<App />,
document.getElementById('root') || document.createElement('div')
);
Extending on dcastil's answer, here's how to skip these trivial files for a TypeScript project:
Edit package.json
At the root level add the following snippet
{
...rest of existing props,
"jest": {
"collectCoverageFrom": [
"src/**/*.{ts,tsx}",
"!src/serviceWorker.ts",
"!src/index.tsx"
]
}
}
Save and re-run coverage
By now coverage should be higher.
Inspired by the answer above by Shiraz. This is essentially the same solution but for React 17/18. Also it provides some additional test coverage.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode />,
);
import React from 'react';
import ReactDOM from 'react-dom';
const render= jest.fn().mockName('render');
jest.mock('react');
jest.mock('react-dom', () => ({
createRoot: jest.fn().mockName('createRoot')
}));
let documentSpy=jest.spyOn(document, 'getElementById')
describe('Entry point index test', () => {
const doc =document.createElement('div');
doc.setAttribute('id', 'root');
beforeEach(() => {
ReactDOM.createRoot.mockReturnValue({render});
require("../index.js");
});
it('should call ReactDOM.createRoot once', () => {
expect(ReactDOM.createRoot).toHaveBeenCalledTimes(1);
});
it('should call document.getElementById with root once', () => {
expect(documentSpy).toHaveBeenCalledTimes(1);
expect(documentSpy).toHaveBeenCalledWith('root');
});
it('should call render with React.StrictMode', () => {
expect(render).toHaveBeenCalledTimes(1);
expect(render).toHaveBeenCalledWith( <React.StrictMode />,);
});
});
Here what i did and looks like it works just perfect (100% coverage, app doesn't break):
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';
export const ReactStrictMode = <React.StrictMode>
<App />
</React.StrictMode>
export const rootElement = document.getElementById('root')
ReactDOM.render(
ReactStrictMode,
rootElement
);
and then in index.spec.js:
// src/index.spec.js
/* eslint-env jest */
import React from 'react'
import ReactDOM from 'react-dom'
import { ReactStrictMode, rootElement } from './index'
jest.mock('react-dom', () => ({ render: jest.fn() }))
describe('index.js', () => {
it('renders without crashing', () => {
ReactDOM.render(ReactStrictMode, rootElement)
expect(ReactDOM.render).toHaveBeenCalledWith(ReactStrictMode, rootElement)
})
})
Added a couple of more test cases. Any feedback would be appreciated...
import React from "react";
import { render, cleanup } from "#testing-library/react";
import ReactDOM from "react-dom";
import App from "./App";
afterEach(cleanup);
// jest.mock will mock all the function using jest.fn() that is present inside the react-dom library
jest.mock("react-dom");
describe("Testing Application Root", () => {
it("should render without crashing", () => {
const div = document.createElement("div");
div.id = "root";
document.body.appendChild(div);
require("./index");
expect(ReactDOM.render).toHaveBeenCalledWith(<App />, div);
});
it("should render the app inside div which has root id", () => {
expect(global.document.getElementById("root")).toBeDefined();
});
it("should render App component", () => {
expect(App).toBeDefined();
});
});

Resources