Problem:
I am trying to test the click function of a React stateless component in ES6. However, when using sinon to spy on the handleClick function I get the response...
TypeError: Cannot read property 'goToFullCart' of undefined
I have tried a few other methods. Some of them seem to work when the component is a Class component but all methods seem to fail when using a stateless functional component as follows...
The code:
import React from 'react'
import PropTypes from 'prop-types'
import SVG from '../../../../static/svg/Svg';
import Svgs from '../../../../static/svg/SvgTemplates';
const TestComponent = ({order, router}) => {
const handleClick = (event) => {
event.stopPropagation()
router.push(`/order/${order.orderId}/cart`);
}
return (
<button className="preview-bar" onClick={handleClick}>
<p>Button Content</p>
</button>
)
}
TestComponent.propTypes = {
order: PropTypes.object.isRequired,
router: PropTypes.object.isRequired,
}
export default TestComponent
export { TestComponent }
import React from 'react';
import { shallow, mount } from 'enzyme';
import { expect } from 'chai';
import sinon from 'sinon';
import TestComponent from "./TestComponent"
let wrapper, props;
describe('<TestComponent /> component unit tests', () => {
describe('when the minimized cart is clicked', () => {
beforeEach(function() {
props = {
}
wrapper = shallow((<TestComponent {...props} />))
});
it('should have a goToFullCart method', () => {
const spy = sinon.spy(TestComponent.prototype, 'handleClick');
expect(spy).to.equal(true)
});
});
});
I am at a loss as to how to spy on the handleClick method to check when the button is clicked.
Thanks for the help and discussion. Feel free to ask for any clarification if anything is not clear.
Cheers
A stateless component is basically a function. Good practice not to create another function inside this because it will create a closure, if you wanted to create methods inside component create stateful component rather
Instead of testing handleClick, you can test route is changed or not
import React from 'react';
import {shallow} from 'enzyme';
test('should call handleClick method when button got clicked', () => {
window.location.assign = mock;
wrapper .find('button').simulate('click');
expect(window.location.assign).toHaveBeenCalled(1);
});
Hope this help you!
Related
I have one component that that use one customized hook and I need to write some test and make sure I mock the hook. How can I mock the customized hook to have unit test for my component like the code of hook does not even exist?
import "./styles.css";
import useAPICall from "#src/hooks/useAPICall ";
export default function App() {
const { onAPICall } = useAPICall(123);
const handleOnClick = useCallback(() => {
onAPICall();
});
return (
<div className="App">
<button onClick={handleOnClick}>Click</button>
</div>
);
}
///test.tsx all I know is this. but I am not sure how I can use mockImplementation that does not return anything
jest.mock('#src/hooks/useAPICall', () => ({
onAPICall: () => jest.fn(),
}));
You can do it in three simple steps:
Import the module which you want to mock
Then mock the module
Provide the return value of the mocked module.
import React from 'react';
import { render } from '#testing-library/react';
import userEvent from '#testing-library/user-event';
import App from './App';
import useAPICall from "#src/hooks/useAPICall "; // 1st step
jest.mock('#src/hooks/useAPICall'); //2nd step
test('can call useAPICall hook', () => {
useAPICall.mockReturnValue({ onAPICall: jest.fn() }); // 3rd step
const { getByRole } = render(<NewApp />);
userEvent.click(getByRole('button'));
expect(useAPICall).toBeCalled();
});
I have a component that is currently wrapped with withRouter (e.g. I use export default withRouter(myComponent)) since I need am using history.push for one of my links within the component. I am writing a test in Enzyme that tests whether a button in that component changes its state to true/false when the user clicks it. The test is failing with the error that it cannot read the property of isExpanded of null. This is what I have for my test:
import React from 'react';
import Adapter from 'enzyme-adapter-react-16';
import { mount, configure } from 'enzyme';
import { MemoryRouter } from 'react-router-dom';
import myComponent from './myComponent';
configure({ adapter: new Adapter() });
describe('Successful flows', () => {
test('button changes state when clicked', () => {
const wrapper = mount(<MemoryRouter><myComponent /></MemoryRouter>);
const moreBtn = wrapper.find('.seeMoreButton').at(0);
moreBtn.simulate('click');
expect(wrapper.state().isExpanded).toEqual(true);
});
});
I have found that before I used withRouter and just had const wrapper = mount(<myComponent />); in my test, the test passed. I am fairly new to routing and I feel like there's something I'm missing here so any help would be appreciated.
You are checking the state of the wrong component, the result of mount will be MemoryRouter, not myComponent.
After you mount the component, you'll need to find myComponent and verify its state instead
test('button changes state when clicked', () => {
const wrapper = mount(<MemoryRouter><myComponent /></MemoryRouter>);
const comp = wrapper.find(myComponent);
const moreBtn = comp.find('.seeMoreButton').at(0);
moreBtn.simulate('click');
expect(comp.state().isExpanded).toEqual(true);
});
I want to write an integration test to assert that a when a parent component drills certain values or properties to a child component, that component receives said values and renders them properly. Below I have two component examples and an example test. Of course, the test is not accurate, but I'm wondering how I can use enzyme to accomplish this? Thanks!
sampleComponent.js:
import React from 'react';
const SampleComponent = () => (
<div test-attr="div">
<SampleChildComponent title="Sample title" />
</div>
);
export default SampleComponent;
sampleChildComponent.js:
import React from 'react';
const SampleChildComponent = ({ title }) => <h3 test-attr="h">{title}</h3>;
export default SampleChildComponent;
sampleComponent.test.js:
import React from 'react';
import { shallow } from 'enzyme';
import SampleComponent from './sampleComponent';
import SampleChildComponent from './sampleChildComponent';
test('renders component without errors', () => {
const wrapper = shallow(<SampleComponent />);
const childWrapper = shallow(<SampleChildComponent />);
expect(childWrapper.text()).toEqual('sample title');
});
To render child components you should use mount instead of shallow:
import { mount } from 'enzyme'
import React from 'react'
import SampleChildComponent from './sampleChildComponent'
import SampleComponent from './sampleComponent'
test('renders component without errors', () => {
const wrapper = mount(<SampleComponent />)
expect(wrapper.find(SampleChildComponent).text()).toEqual('sample title')
})
I am trying to test my component which is consuming data from context via HOC.
Here is setup:
Mocked context module /context/__mocks__
const context = { navOpen: false, toggleNav: jest.fn() }
export const AppContext = ({
Consumer(props) {
return props.children(context)
}
})
Higher OrderComponent /context/withAppContext
import React from 'react'
import { AppContext } from './AppContext.js'
/**
* HOC with Context Consumer
* #param {Component} Component
*/
const withAppContext = (Component) => (props) => (
<AppContext.Consumer>
{state => <Component {...props} {...state}/>}
</AppContext.Consumer>
)
export default withAppContext
Component NavToggle
import React from 'react'
import withAppContext from '../../../context/withAppContext'
import css from './navToggle/navToggle.scss'
const NavToggle = ({ toggleNav, navOpen }) => (
<div className={[css.navBtn, navOpen ? css.active : null].join(' ')} onClick={toggleNav}>
<span />
<span />
<span />
</div>
)
export default withAppContext(NavToggle)
And finally Test suite /navToggle/navToggle.test
import React from 'react'
import { mount } from 'enzyme'
beforeEach(() => {
jest.resetModules()
})
jest.mock('../../../../context/AppContext')
describe('<NavToggle/>', () => {
it('Matches snapshot with default context', () => {
const NavToggle = require('../NavToggle')
const component = mount( <NavToggle/> )
expect(component).toMatchSnapshot()
})
})
Test is just to get going, but I am facing this error:
Warning: Failed prop type: Component must be a valid element type!
in WrapperComponent
Which I believe is problem with HOC, should I mock that somehow instead of the AppContext, because technically AppContext is not called directly by NavToggle component but is called in wrapping component.
Thanks in advance for any input.
So I solved it.
There were few issues with my attempt above.
require does not understand default export unless you specify it
mounting blank component returned error
mocking AppContext with __mock__ file caused problem when I wanted to modify context for test
I have solved it following way.
I created helper function mocking AppContext with custom context as parameter
export const defaultContext = { navOpen: false, toggleNav: jest.fn(), closeNav: jest.fn(), path: '/' }
const setMockAppContext = (context = defaultContext) => {
return jest.doMock('../context/AppContext', () => ({
AppContext: {
Consumer: (props) => props.children(context)
}
}))
}
export default setMockAppContext
And then test file ended looking like this
import React from 'react'
import { shallow } from 'enzyme'
import NavToggle from '../NavToggle'
import setMockAppContext, { defaultContext } from '../../../../testUtils/setMockAppContext'
beforeEach(() => {
jest.resetModules()
})
describe('<NavToggle/>', () => {
//...
it('Should have active class if context.navOpen is true', () => {
setMockAppContext({...defaultContext, navOpen: true})
const NavToggle = require('../NavToggle').default //here needed to specify default export
const component = shallow(<NavToggle/>)
expect(component.dive().dive().hasClass('active')).toBe(true) //while shallow, I needed to dive deeper in component because of wrapping HOC
})
//...
})
Another approach would be to export the component twice, once as decorated with HOC and once as clean component and create test on it, just testing behavior with different props. And then test just HOC as unit that it actually passes correct props to any wrapped component.
I wanted to avoid this solution because I didn't want to modify project file(even if it's just one word) just to accommodate the tests.
I'm trying to test if a function is called in the componentDidMount hook of my component.
I use React-Native and Jest to test my component.
The component looks like this:
const tracker = new GoogleAnalyticsTracker('UA-79731-33');
class MyComponent extends Component {
componentDidMount() {
tracker.trackScreenView(this.props.title);
}
}
So I'm mocking the GoogleAnalyticsTracker, it looks okay. Although I'm not sure how I can test that it has been called in the componentDidMount hook.
This is my test, which doesn't work:
import 'react-native';
import React from 'react';
import renderer from 'react-test-renderer';
import { GoogleAnalyticsTracker } from 'react-native-google-analytics-bridge';
import MyComponent from '../';
jest.mock('react-native-google-analytics-bridge');
const tracker = new GoogleAnalyticsTracker('ABC-123');
describe('MyComponent', () => {
it('renders', () => {
const tree = renderer.create(
<MyComponent />,
).toJSON();
expect(tree).toMatchSnapshot();
expect(tracker.trackScreenView).toBeCalled();
});
});
The toBeCalled() returns false.
How can I test that my function has been called in the componentDidMount?
Thanks
The react test rendered only calls the render method of a component and returns the output, it does not really start the component and so all the life cycle methods are not called. You should switch to the enzyme renderer which supports the full start of components using enzyme.mount