Can we mock a function inside a node module component? - javascript

I have a checkbox component coming as a node module to my app,
CHECKBOX Component:
import React from 'react';
import uuidv4 from 'uuid/dist/v4';
export default ({inputProps}) => {
let id = uuidv4();
return (
<input
type="checkbox"
{...inputProps}
id={id}
/>
);
};
In my App.js, I'm using CheckBox Component
import React from 'react';
import Checkbox from '#checkbox'; // path from the node module
export default () => {
return (
<Checkbox
{...{
inputProps: {
checked: false,
onChange: () => {}
}
}}
/>
)
};
Test file:
import {mount} from 'enzyme';
describe('test App', () => {
it('should render the component', async () => {
const component = mount(<App />);
expect(component).toBeDefined();
expect(component.html()).toMatchSnapshot();
});
});
These are the sample components I've added
I don't want to mock the entire Checkbox component coz I want to test the checkbox behavior.
Is there any way that I can mock the uuid function in Checkbox component since it's generating a new UUID every single time I update the snapshot without any actual component changes.
I tried mocking the UUID function
jest.mock('uuid/dist/v4', () => {
return () => 'test id';
});
This mocks any of the usages in my app but not the function inside the Checkbox component or any other node module.
please ignore if any imports missed
I'm able to run the tests successfully but I want to find out a way to ignore the UUID generation in snapshots files.

You probably shouldn't import from package/dist/, try
import { v4: uuidv4 } from 'uuid';
and to mock, try using this:
jest.mock("uuid", () => {
return {
v4: () => 'test id'
}
});
See also a playground (it works): https://repl.it/#kiprasmel/jest-playground

Related

Keep getting error message about avoiding wrapping code in act after wrapping code in act

I'm writing a test for a component that uses a search form. This is in search.js and it imports a Formik form from another file searchForm.js.
I've written this unit test which passes. I've based it off an example in the react testing docs here.
Every time I run it I get this error, which confuses me since I've wrapped all the code in act?
console.error
Warning: An update to Formik inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
import React from "react";
import { render, screen, act, fireEvent } from "#testing-library/react";
import Search from "./search.js";
import { ChakraProvider } from "#chakra-ui/react";
test("Search form input", () => {
act(() => {
const setup = () => {
const utils = render(
<ChakraProvider>
<Search />
</ChakraProvider>
);
const searchTermsInput = utils.getByLabelText("search terms input");
return {
searchTermsInput,
...utils,
};
};
const { searchTermsInput } = setup();
fireEvent.change(searchTermsInput, { target: { value: "23" } });
});
expect(screen.getByLabelText("search terms input").value).toBe("23");
});
As per the comment above, fixed this by rewriting the test as follows to allow for some other changes to state that happen in the search.js component which weren't part of this specific test.
import React from "react";
import { render, screen, act, fireEvent, waitFor } from "#testing-library/react";
import Search from "./search.js";
import { ChakraProvider } from "#chakra-ui/react";
test("Search form input", async () => {
act(() => {
const setup = () => {
const utils = render(
<ChakraProvider>
<Search />
</ChakraProvider>
);
const searchTermsInput = utils.getByLabelText("search terms input");
return {
searchTermsInput,
...utils,
};
};
const { searchTermsInput } = setup();
fireEvent.change(searchTermsInput, { target: { value: "23" } });
});
expect(screen.getByLabelText("search terms input").value).toBe("23");
// This removes a code not wrapped in act warning triggered by other state events
// namely setQuery which is called after adding search term inputs
await waitFor(() => screen.queryByText(/Find results/))});

Jest manual mock not getting executed for Higher Order Function

I am new to Jest, I am trying to mock my context HOC function to test my component
Here is my folder structure
Folder Structure
Basically I am trying to mock the homeContext.js file which is being used by my HomeScreen component. As per the docs I have created a __mocks__ folder under the contexts folder and created a homeContext.js file
Here are the contents of the files:
/contexts/__mocks__/homeContext.js
const mock = jest.fn().mockImplementation(() => {
console.log('Mocking');
return {
withHomeContext: jest.fn(() => {}),
};
});
module.exports = mock;
contexts/homeContext.js
export const withHomeContext = ChildComponent => props => {
console.log('Not Hitting');
return (
<HomeContext.Consumer>
{context => <ChildComponent {...props} homeContext={context} />}
</HomeContext.Consumer>
);
};
Here is how I am trying to use it in my scenes/home/index.test.js
jest.mock('../../contexts/homeContext');
import React from 'react';
import {mount} from 'enzyme';
import HomeScreen from './index';
describe('<HomeScreen /> renders', () => {
let wrapper;
beforeEach(() => {
wrapper = mount(<HomeScreen />);
expect(wrapper).toBeDefined();
});
})
This is how the component which I am testing uses it
import * as React from 'react';
//Context
import {withHomeContext} from '../../contexts/homeContext';
class HomeScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
relatedItems: [],
};
}
render() {
//Some render logic
}
}
export default withHomeContext(HomeScreen);
Please help me why my __mocks__/homeContext.js never gets hit, it is hitting the actual file, which console logs ('Not Hitting')

How to test mapStateToProps using React Redux and Jest?

When I create a test for my connected React component where I want to test the mapStateToProps logic I run into a problem which I'm not sure how to solve.
Error message
Expected: 1
Received: undefined
24 | it('should show previously rolled value', () => {
25 | // test that the state values were correctly passed as props
> 26 | expect(wrapper.props().lastRolledNumber).toBe(1);
When I check the wrapper.props() it only returns the store object and no properties.
To make sure it's not my code I have found an example that should work to simplify it, but I get the same problem when using that exact version in my app (option #2, https://jsramblings.com/2018/01/15/3-ways-to-test-mapStateToProps-and-mapDispatchToProps.html)
I think it might have to do with the React 16+ version, which I found mentioned here: https://airbnb.io/enzyme/docs/api/ReactWrapper/props.html
.props() => Object
Returns the props object for the root node of the wrapper. It must be
a single-node wrapper. This method is a reliable way of accessing the
props of a node; wrapper.instance().props will work as well, but in
React 16+, stateless functional components do not have an instance.
See .instance() => ReactComponent
Does anyone know how to test this in a good way to see that the logic is working as expected without exporting the private mapStateToProps function directly?
Component
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
// Component 1 - "Base component"
// Exporting it is a good practice for testing its own logic
export const Dice = ({ lastRolledNumber, onRollDice }) => (
<div>
<p>The last rolled number was {lastRolledNumber}.</p>
<button onClick={onRollDice}>Roll dice</button>
</div>
);
Dice.propTypes = {
lastRolledNumber: PropTypes.number.isRequired,
onRollDice: PropTypes.func.isRequired
}
const mapStateToProps = (state) => ({
lastRolledNumber: state.lastRolledNumber
});
const mapDispatchToProps = (dispatch) => ({
onRollDice: () => dispatch({ type: 'ROLL_DICE' })
});
// Component 2 - Container component
// Export it as a default export
export default connect(mapStateToProps, mapDispatchToProps)(Dice);
Test
import React from 'react';
import { shallow } from 'enzyme';
import '../test-config'; // Setup Enzyme & Adapter
import DiceContainer from './Dice';
// Create the mock store
import configureMockStore from 'redux-mock-store';
const mockStore = configureMockStore();
describe('Dice', () => {
let wrapper, store;
beforeEach(() => {
const initialState = {
lastRolledNumber: 1
};
store = mockStore(initialState);
// Shallow render the container passing in the mock store
wrapper = shallow(
<DiceContainer store={store} />
);
});
it('should show previously rolled value', () => {
// test that the state values were correctly passed as props
expect(wrapper.props().lastRolledNumber).toBe(1);
});
it('should roll the dice again when button is clicked', () => {
// test that the component events dispatch the expected actions
wrapper.simulate('rollDice');
const actions = store.getActions();
expect(actions).toEqual([ { type: 'ROLL_DICE' } ]);
});
});
You are almost there. You need to call .dive() method so that you can get the Dice component rather than the DiceContainer component which is wrapped by the connect HOC of react-redux module.
Short answer:
wrapper = shallow(<DiceContainer store={store} />).dive();
A Completed working example:
index.jsx:
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
export const Dice = ({ lastRolledNumber, onRollDice }) => (
<div>
<p>The last rolled number was {lastRolledNumber}.</p>
<button onClick={onRollDice}>Roll dice</button>
</div>
);
Dice.propTypes = {
lastRolledNumber: PropTypes.number.isRequired,
onRollDice: PropTypes.func.isRequired,
};
const mapStateToProps = (state) => ({
lastRolledNumber: state.lastRolledNumber,
});
const mapDispatchToProps = (dispatch) => ({
onRollDice: () => dispatch({ type: 'ROLL_DICE' }),
});
export default connect(mapStateToProps, mapDispatchToProps)(Dice);
index.test.jsx:
import React from 'react';
import { shallow } from 'enzyme';
import configureMockStore from 'redux-mock-store';
import DiceContainer from '.';
const mockStore = configureMockStore();
describe('Dice', () => {
let wrapper;
let store;
beforeEach(() => {
const initialState = {
lastRolledNumber: 1,
};
store = mockStore(initialState);
wrapper = shallow(<DiceContainer store={store} />).dive();
});
it('should show previously rolled value', () => {
expect(wrapper.props().lastRolledNumber).toBe(1);
});
it('should roll the dice again when button is clicked', () => {
wrapper.simulate('rollDice');
const actions = store.getActions();
expect(actions).toEqual([{ type: 'ROLL_DICE' }]);
});
});
Unit test results:
PASS src/stackoverflow/59771991/index.test.jsx (9.645s)
Dice
✓ should show previously rolled value (19ms)
✓ should roll the dice again when button is clicked (2ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 11.505s
Test coverage html report:
In my case, I was able to write the test with render by this -
import React from 'react';
import '#testing-library/jest-dom';
import {
screen, cleanup, render,
} from '#testing-library/react';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
const store = configureMockStore()({
headerState: {
showBack: false,
},
});
const mockProps = { ... };
describe('Component', () => {
beforeAll(() => {
render(
<Provider store={store}>
<Component {...mockProps} />
</Provider>,
);
});
afterAll(cleanup);
test('Test', () => {
screen.debug();
});
});

Jest/Enzyme Class Component testing with React Suspense and React.lazy child component

So I converted an import used in a class component to React.lazy import api and wrapped it in a Suspense tag. When I test that class component, enzyme throws an error "Enzyme Internal Error: unknown node with tag 13". Is there a way to render and test the mount of the lazy loaded component rather than using shallow render?
I've already tried async await to wait until the promise of the lazy load resolved but that didn't work neither, like so:
it('async await mount', () async () => {
const wrapper = await mount(<Component />)
}
here's example code:
Component.js
import React, { PureComponent, Suspense } from 'react'
const ChildComponent = React.lazy(() => import('../ChildComponent'))
export default class Component extends PureComponent {
constructor() {
super()
this.state = {
example: null
}
}
doSomething = () => this.setState({
example: 'example'
})
render() {
return (
<div>
<p>Example</p>
<Suspense fallback={<div>...loading</div>}>
<ChildComponent
example={this.state.example}
doSomething={this.doSomething}
/>
</Suspense>
</div>
)
}
}
Component.test.js
import React from 'react'
import renderer from 'react-test-renderer'
import { mount } from 'enzyme'
import Component from '../../Component'
describe('Component', () => {
// snapshot renders loading and not children
it('example snapshot of tree', () => {
const tree = renderer.create(<Component />).toJSON()
expect(tree).toMatchSnapshot()
})
it('example mount test', () => {
// this test fails and throws error I state above
const wrapper = mount(<Component />)
wrapper.setState({ example: 'example' })
expect(wrapper.state.example).toBe('example')
})
})
I read that Enzyme does not support React 16.6 Suspense API yet but I wanted to know if there was still a way to test the mounted so I can use things like simulate and find API from Enzyme.
Solution
The GitHub issue linked by ChuckJHardy has been resolved merged and released. You are now able to use the mount API in enzyme as of 1.14.0.
References
https://github.com/airbnb/enzyme/pull/1975
I needed to test my lazy component using Enzyme. Following approach worked for me to test on component loading completion:
const myComponent = React.lazy(() =>
import('#material-ui/icons')
.then(module => ({
default: module.KeyboardArrowRight
})
)
);
Test Code ->
//mock actual component inside suspense
jest.mock("#material-ui/icons", () => {
return {
KeyboardArrowRight: () => "KeyboardArrowRight",
}
});
const lazyComponent = mount(<Suspense fallback={<div>Loading...</div>}>
{<myComponent>}
</Suspense>);
const componentToTestLoaded = await componentToTest.type._result; // to get actual component in suspense
expect(componentToTestLoaded.text())`.toEqual("KeyboardArrowRight");
This is hacky but working well for Enzyme library.

shallow rendering could not find component

hi everyone I am testing my react application using jest. While testing a component I found that a test breaks unexpectedly throwing error as
Method “props” is only meant to be run on a single node. 0 found instead.
test file
import React from 'react';
import {shallow} from 'enzyme';
import {AddLibraryItem} from '../../components/AddLibraryItem';
import libraryItems from '../fixtures/libraryItems';
let addLibraryItem, history, wrapper;
beforeEach(() => {
addLibraryItem = jest.fn();
history = {push: jest.fn()};
wrapper = shallow(<AddLibraryItem addLibraryItem={addLibraryItem} history={history}/>);
})
test('should execute on submit button successfully', () => {
console.log(wrapper);
wrapper.find('LibraryItemForm').prop('onSubmit')(libraryItems[0]);
expect(addLibraryItem).toHaveBeenLastCalledWith(libraryItems[0]);
expect(history.push).toHaveBeenLastCalledWith("/");
});
Component
import React from 'react';
import {connect} from 'react-redux';
import LibraryItemForm from './LibraryItemForm';
import {addLibraryItem} from '../actions/libraryA';
export class AddLibraryItem extends React.Component {
onSubmit = (libraryItem) => {
this.props.addLibraryItem(libraryItem);
this.props.history.push('/');
}
render () {
return (
<div>
<LibraryItemForm onSubmit={this.onSubmit} />
</div>
);
}
}
const mapDispatchToProps = (dispatch) => {
return {
addLibraryItem: (libraryItem) => dispatch(addLibraryItem(libraryItem))
}
}
const ConnectedAddLibraryItem = connect(undefined, mapDispatchToProps)(AddLibraryItem);
export default ConnectedAddLibraryItem;
The piece of test was earlier working very fine and test of 'LibraryItemForm' is also working fine and also rendering perfectly.
I am not getting what is wrong with it.
Is there any fix of it?
You probably forgot to dive():
wrapper.find(LibraryItemForm).dive().prop('onSubmit')(libraryItems[0]);
Enzyme documentation here.

Categories

Resources