How to properly Assert calling a React function using Jest - reactjs

I've been trying to test if a React function has been called on Click but i always get the same result :
Expected number of calls: >= 1
Received number of calls: 0
And the test fails, this is my Setup,
This is MyComponent
import React from 'react';
export default function MyComponent() {
function handleClick() {
console.log('Clicking');
}
return (
<div>
<button onClick={handleClick}>Click</button>
</div>
);
}
And here is my Jest Test
import { render, screen, fireEvent } from '#testing-library/react';
import MyComponent from './MyComponent';
describe('It should run the tests', () => {
it('Should call the funct', () => {
//Arrange
render(<MyComponent />);
const handleClick = jest.fn();
const button = screen.getByRole('button');
//Action
fireEvent.click(button);
//Assert
expect(handleClick).toHaveBeenCalled();
});
});
This works normally on the app, it does call the function and logs 'Clicking'
Thanks in advance!

You have defined the mock function but you're Component doesn't know anything about it since it's defined within the component.
What you could do is pass the onClick as a prop to the Component. This way you could pass any handler function required for the Component as a prop.
import React from 'react';
export default function MyComponent({ onClick }) { // 👈 receive as a prop
/* function handleClick() {
console.log('Clicking');
} */
return (
<div>
<button onClick={onClick}>Click</button> {/* 👈 call the prop */}
</div>
);
}
Update test:
import { render, screen, fireEvent } from '#testing-library/react';
import MyComponent from './MyComponent';
describe('It should run the tests', () => {
it('Should call the funct', () => {
//Arrange
const handleClick = jest.fn();
render(<MyComponent onClick={handleClick} />); // 👈 pass the mock
const button = screen.getByRole('button');
//Action
fireEvent.click(button);
//Assert
expect(handleClick).toHaveBeenCalled();
});
});

Related

How to check if a component disappears after click using jest & enzyme?

I have a component,
function TestComponent() {
const [visible, setVisible] = useState(true);
return (
<React.Fragment>
{visible && <Container>
I'm Visible
<button onClick={() => setVisible(false)}>
click to close
</button>
</Container>}
</React.Fragment>
);
}
I'm trying to test that on clicking the button the component should be invisible.
And I have following test case to test that,
test('Random Test', () => {
const randomComponent = shallow(<TestComponent />);
expect(randomComponent.find('Container')).toBeTruthy();
randomComponent.find('button').simulate('click');
expect(randomComponent.find('Container')).toBeFalsy();
});
Doesnt seem to work,
Getting error,
expect(received).toBeFalsy()
Received: {}
Any help would be appreciated?
I have a sneaky suspicion that this isnt the way to check if component is hidden. Would also appreciate if anyone could tell a better way.
Update #1:
expect(randomComponent.render().text()).toContain('I\'m Visible');
randomComponent.find('button').simulate('click');
expect(randomComponent.render().text()).toContain('');
Using the above testcases seem to work. Still looking for a better way.
Since .find() will always return an instance of ShallowWrapper class even if there is no node matched. See source code of .find() and .wrap(). It will NOT return a falsy value (null, undefined), so .toBeFalsy() assertion will always fail.
Use .exists([selector]) => Boolean
Returns whether or not any nodes exist in the wrapper. Or, if a selector is passed in, whether that selector has any matches in the wrapper.
import { shallow } from 'enzyme';
import React from 'react';
import { TestComponent } from './';
describe('68334346', () => {
it('should pass', () => {
const randomComponent = shallow(<TestComponent />);
expect(randomComponent.find('Container').exists()).toBeTruthy();
randomComponent.find('button').simulate('click');
expect(randomComponent.find('Container').exists()).toBeFalsy();
});
});
Use .toHaveLength(number) matcher
import { shallow } from 'enzyme';
import React from 'react';
import { TestComponent } from './';
describe('68334346', () => {
it('should pass', () => {
const randomComponent = shallow(<TestComponent />);
expect(randomComponent.find('Container')).toHaveLength(1);
randomComponent.find('button').simulate('click');
expect(randomComponent.find('Container')).toHaveLength(0);
});
});

React jest enzyme function called test

Hi I have a simple component I need to test:
MyComponent.js-----
import React from 'react';
const MyComponent = (props) => {
onClickHandler = () => {
console.log('clicked');
props.outsideClickHandler();
}
return (
<div>
<span className='some-button' onClick={onClickHandler}></span>
</div>
);
}
MyComponent.test.js----
import React from 'react';
import { shallow } from 'enzyme';
import MyComponent from './MyComponent';
describe('MyComponent', () => {
const onClickHandler = jest.fn();
it('calls click event', () => {
const wrapper = shallow(<MyComponent />);
wrapper.find('.some-button').simulate('click');
expect(onClickHandler.mock.calls.length).toEqual(1); // tried this first
expect(onClickHandler).toBeCalled(); // tried this next
});
});
Tried above two types of expect, my console log value is coming
console.log('clicked'); comes
but my test fails and I get this:
expect(received).toEqual(expected) // deep equality
Expected: 1
Received: 0
So, the problem with your code is when you simulate a click event, you expect a totally independent mock function to be called. You need to attach the mock function to the component. The best way is using prototype. Like this:
it('calls click event', () => {
MyComponent.prototype.onClickHandler = onClickHandler; // <-- add this line
const wrapper = shallow(<MyComponent />);
wrapper.find('.some-button').simulate('click');
expect(onClickHandler.mock.calls.length).toEqual(1);
expect(onClickHandler).toBeCalled();
expect(onClickHandler).toHaveBeenCalledTimes(1); // <-- try this as well
});
Refer to this issue for more potential solutions.

How can i test with Enzyme+Mocha method call inside Component

Cannot figure out how to test a component method with enzyme + mocha + sinon. I want to test if component calls method loadPosts on button click.
import React from 'react';
import { configure, mount} from 'enzyme';
import { expect } from 'chai';
import Adapter from 'enzyme-adapter-react-16';
import { Posts } from '../Components/Posts';
import sinon from 'sinon';
configure({ adapter: new Adapter() });
describe('Posts', () => {
let wrapper;
let inst;
beforeEach(() => {
wrapper = mount(<Posts />);
inst = wrapper.instance();
sinon.spy(inst, 'loadPosts');
wrapper.find('button').simulate('click');
});
it('should load posts on button click', () => {
wrapper.update();
expect(inst.loadPosts).to.have.property('callCount', 1);
});
it('should set `loading` to true', () => {
expect(wrapper.state('loading')).to.equal(true);
});
});
And here is my component:
import React, {Component} from 'react';
import axios from 'axios';
export class Posts extends Component {
state = {
posts: null,
loading: false
}
componentDidMount() {}
loadPosts = () => {
this.setState({loading: true}, () => {
axios.get('https://jsonplaceholder.typicode.com/todos')
.then( d => this.setState({
posts: d.data
}));
});
}
render() {
return (<div>
<h4>I am posts</h4>
<button onClick= {this.loadPosts}>Load posts</button>
</div>);
}
}
But my test fails with error: Exception error: expected [Function] to have property 'callCount' of 1 but got 0
The onClick of your button gets bound directly to whatever this.loadPosts is when the component renders.
When you replace loadPosts with the spy, it doesn't have any effect on the currently rendered button so onClick doesn't call your spy.
The two options for fixing it are to call this.loadPosts using an arrow function like this:
<button onClick={() => this.loadPosts()}>Load posts</button>
...so that when onClick gets called it calls whatever this.loadPosts is currently set to.
The other option is to force a re-render after you create your spy so onClick gets bound to your spy instead of the original function.

How to test function that passed from mapDispatchToProps (React/Redux/Enzyme/Jest)

I want to test that function passed from mapDispatchToProps was invoked when button clicking is simulated.
How to test that function which passed from mapDispatchToProps is invoked?
I tried to pass a mocked function by props, but it doesn't work. Any help will be appreciated.
Here below my fake class code and test example.
My component
// All required imports
class App extends React.Component<Props> {
render() {
const { onClick } = this.props;
return (
<>
<h1>Form</h1>
<input />
<button onClick={() => onClick()} />
</>
);
}
}
const mapStateToProps = (state) => {
return {
state
};
};
const mapDispatchToProps = (dispatch) => {
return {
onClick: () => dispatch(actions.onClick())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
My test file
import { configure, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16/build/index';
import jsdom from 'jsdom';
import React from 'react';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import ConnectedApp, { App } from './App';
function setUpDomEnvironment() {
const { JSDOM } = jsdom;
const dom = new JSDOM('<!doctype html><html><body></body></html>', { url: 'http://localhost/' });
const { window } = dom;
global.window = window;
global.document = window.document;
global.navigator = {
userAgent: 'node.js',
};
copyProps(window, global);
}
function copyProps(src, target) {
const props = Object.getOwnPropertyNames(src)
.filter(prop => typeof target[prop] === 'undefined')
.map(prop => Object.getOwnPropertyDescriptor(src, prop));
Object.defineProperties(target, props);
}
setUpDomEnvironment();
configure({ adapter: new Adapter() });
const mockStore = configureMockStore();
describe('App', () => {
describe('When App connected to store', () => {
describe('When button clicked', () => {
it('should not crush after click on login button', () => {
const onClick = jest.fn()
const store = mockStore(initialStates[1]);
const wrapper = mount(
<Provider store={store}>
<ConnectedApp />
</Provider>);
wrapper.find('button').simulate('click');
??? how to test that function passed from mapDispatchToProps was fired?
});
});
});
});
I recommend following the approach described in the docs and export the connected component as the default export for use in the application, and export the component itself as a named export for testing.
For the code above export the App class and test the click like this:
import * as React from 'react';
import { shallow } from 'enzyme';
import { App } from './code';
describe('App', () => {
it('should call props.onClick() when button is clicked', () => {
const onClick = jest.fn();
const wrapper = shallow(<App onClick={onClick} />);
wrapper.find('button').simulate('click');
expect(onClick).toHaveBeenCalledTimes(1);
});
});
shallow provides everything that is needed for testing the component itself. (shallow even calls React lifecycle methods as of Enzyme v3)
As you have found, to do a full rendering of the component requires a mock redux store and wrapping the component in a Provider. Besides adding a lot of complexity, this approach also ends up testing the mock store and all child components during the component unit tests.
I have found it much more effective to directly test the component, and to export and directly test mapStateToProps() and mapDispatchToProps() which is very easy since they should be pure functions.
The mapDispatchToProps() in the code above can be tested like this:
describe('mapDispatchToProps', () => {
it('should dispatch actions.onClick() when onClick() is called', () => {
const dispatch = jest.fn();
const props = mapDispatchToProps(dispatch);
props.onClick();
expect(dispatch).toHaveBeenCalledWith(actions.onClick());
});
});
This approach makes unit testing the component very simple since you can pass the component props directly, and makes it very simple to test that the component will be handed the correct props by the pure functions (or objects) passed to connect().
This ensures that the unit tests are simple and targeted. Testing that connect() and redux are working properly with the component and all of its child components in a full DOM rendering can be done in the e2e tests.

Wait for condition in Enzyme and Jest (eventual consistency, assert with timeout)

I have a simple test:
import React from 'react';
import GenericButton from 'components/buttons/GenericButton';
import { shallow } from 'enzyme';
import { shallowToJson } from 'enzyme-to-json';
describe('Generic Button', () => {
test('Button action called when clicked', () => {
var clicked = false;
const component = shallow(
<GenericButton action={() => {
clicked = true;
}}
id="testComponent"/>
);
component.find('testComponent').first().simulate('click');
expect(clicked).toBeTruthy();
});
});
However this will fail as the action is done eventually,
If i set the assertion to happen eventually (using setTimeout for example) it will work
however it would be better if i do something of the following before the assert (found this on examples using jasmine)
waitsFor(() => {
return clicked;
}, "the value to change", 1000);
What is the equivalent call for enzyme/jest?
Edit: Content of component
render() {
return <Button id={this.props.id}
key={this.props.id}
onClick={() => this.props.action()}
bsStyle={this.props.style}>
{this.props.text}
</Button>;
}
(Button is a 3rd party library component)
To test if a click handler was added correctly pass a spy into your comment, simulate the click and check that the spy was called. Doing this there is no need to use waitsFor.
import React from 'react';
import GenericButton from 'components/buttons/GenericButton';
import { shallow } from 'enzyme';
import { shallowToJson } from 'enzyme-to-json';
describe('Generic Button', () = > {
test('Button action called when clicked', () = > {
const action = jest.fn();
const component = shallow(
<GenericButton action={action}
id="testComponent"/>
);
component.find('Button').first().simulate('click');
expect(action).toHaveBeenCalled();
});
});

Resources