How to test a handler containing setState - reactjs

I have a component ButtonTest.js
import React from 'react';
class ButtonTest extends React.Component {
constructor() {
super();
this.state = {
disabled: false,
};
}
handleClick = () => {
this.setState({
disabled: !this.state.disabled,
});
}
render() {
return (
<div>
<button onClick={this.handleClick}>
First Button
</button>
<button disabled={this.state.disabled}>
Second Button
</button>
</div>
);
}
}
export default ButtonTest;
Testing code in file ButtonTest.test.js
import React from 'react';
import Enzyme, { shallow, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import ButtonTest from "./ButtonTest";
Enzyme.configure({ adapter: new Adapter()});
describe('<Button />', () => {
it('test buttontest', () => {
const wrapper = shallow(<ButtonTest />);
const firstButton = wrapper.find('button').at(0);
const secondButton = wrapper.find('button').at(1);
firstButton.props().onClick();
expect(secondButton.props().disabled).toEqual(true);
});
});
firstButton.props().onClick(); will trigger handleClick, then setState is called. But, setState is an async function, so the assertion will be called before render. That why the test is FAIL.
How to ensure the render finished before the assertion is called

I update test code like this. I don't define secondButton at the beginning, I define it after clicking finished
describe('<Button />', () => {
it('test buttontest', () => {
const wrapper = shallow(<ButtonTest />);
const firstButton = wrapper.find('button').at(0);
firstButton.props().onClick();
const secondButton = wrapper.find('button').at(1);
expect(secondButton.props().disabled).toEqual(true);
});
});

Related

How do I write state case for the state in react useState using Jest

I am new to Jest, I trying to write test case for state value , I am want to update the state value in test suite, any one can help in this. thanks.
Ex: My Component
import React, {useState} from 'react';
import Child from './Child';
function Parent(props) {
const [isFetching, setState] = useState(false);
return (
<div className="parent">
<h1>Parent</h1>
{isFetching ? 0 : 1}
<Child called={setState} />
{ isFetching ? <div>
<p>from state true</p>
<button onClick={clickmefn}>click me</button>
</div>: null
}
</div>
);
}
export default (Parent);
My Test suite which is failed due not found the element
wrapper.find('p').text() unbale to find this because the is state false.
import Parent from './Parent';
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
import { mount, shallow } from 'enzyme';
const setHookState = (newState) => jest.fn().mockImplementation((state) => [
newState,
(newState) => {}
])
const reactMock = require('react')
describe('ComponentWithHook component', () => {
let wrapper
beforeEach(() => {
wrapper = shallow(<Parent/>)
});
it('should render itself', () => {
const changeCounter = jest.fn();
const handleClick = jest.spyOn(React, "useState");
handleClick.mockImplementation(isFetching => [isFetching, changeCounter]);
expect(wrapper.find('p').text()).toBe('from state true');
})
})
I recommend to do the test the more real as possible, and you can accomplish that without using an extra lib, just using react-dom and react-dom/test-utils.
import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import Parent from './Parent';
describe("App Tests", () => {
let container = null;
beforeEach(() => {
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() => {
unmountComponentAtNode(container);
container.remove();
container = null;
});
it("should render itself", () => {
act(async () => {
render(
<Parent/>,
container
);
});
const childElement = container.querySelector(`.child-element`); // get the child element
act(() => {
childElement.dispatchEvent( // distpach the action to update the state
new MouseEvent("click", { bubbles: true })
);
});
expect(container.querySelector('p')).toBeTruthy();
expect(container.querySelector('p').textContent).toBe("from state true");
});
});
EDIT: // here you can find more info, https://reactjs.org/docs/testing-recipes.html

Unable to stimulate change event in child component React redux jest

I am new to react redux and I am trying to unit test FormContainer component. But I am unable to simulate change event on Form component using enzyme or jest. In wrapper.instance() I am receiving null but I dont' think that should. If not then what is correct way of testing FormContainer.js file, because when I ran test coverage it showed me that change function was uncovered.
FormContainer.js
import React, { Component } from "react";
import { connect } from "react-redux";
import Form from "../components/Form";
import { isNumeric } from "../utils";
class FormContainer extends Component {
constructor(props) {
super(props);
this.state = {
number: "",
error: false
};
}
// componentWillReceiveProps(nextProps) {
// console.log(nextProps, this.props, "main component");
// }
change = event => {
let value = event.target.value;
if (isNumeric(value) && value < 1001 && value >= 0) {
if (value === "0") {
this.setState({
...this.state,
error: true
});
return;
} else {
this.setState({
...this.state,
[event.target.name]: value,
error: false
});
}
} else {
this.setState({ ...this.state, error: true });
}
};
render() {
return (
<Form change={this.change} value={this.state} error={this.state.error} />
);
}
}
const mapStateToProps = state => {
return { ...state };
};
export default connect(mapStateToProps)(FormContainer);
FormContainer.test.js
import React from 'react';
import configureStore from "redux-mock-store";
import { shallow,mount } from 'enzyme';
import FormContainer from '../../containers/FormContainer';
import { findByAttr } from '../../utils';
import Form from '../../components/Form';
describe("Testing Form container", () => {
let wrapper;
let store;
beforeEach(() => {
const initialState = {}
const mockStore = configureStore();
store = mockStore(initialState);
wrapper = shallow(<FormContainer store={store} />);
});
it("Should render without error ", () => {
expect(wrapper.exists()).toBe(true);
});
it("Simulate change", () => {
let component = wrapper.find(Form);
const mockChange = jest.fn();
wrapper.instance().change = mockChange;
});
});
Testing Form container › Simulate change
TypeError: Cannot set property 'change' of null
22 | let component = wrapper.find(Form);
23 | const mockChange = jest.fn();
> 24 | wrapper.instance().change = mockChange;
| ^
25 | });
26 | });
at Object.<anonymous> (src/__test__/containers/FormContainer.test.js:24:9)
Dont need to import import Form from '../../components/Form'; since we are only testing FormContainer component, so remove it.
and there is no event listener called change, Use onChange instead.
<Form onChange={this.change} value={this.state} error={this.state.error} />
Try changing this spec:
it("Simulate change", () => {
let component = wrapper.find(Form);
const mockChange = jest.fn();
wrapper.instance().change = mockChange;
});
To:
it("Simulate change", () => {
(wrapper.find(Form).first()).simulate('change');
expect(wrapper.instance().change.mock.calls.length).toBe(1);
});

Mock function is not identified as called, after a button click inside the <a> tag

I have a local function that should be called on a button click and set the state of a Boolean variable inside it. I tried to add unit test to this module to identify whether the button is clicked and the function is called following the button click.
But my test is failing. I tried by mock the function inside the 'describe' method yet it didn't work.
SomeComponent.js
class SomeComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
openImagesDialog: false,
}
}
fuctionToBeCalled = (val) => {
this.setState({ openImagesDialog: val })
}
render() {
return (
<a onClick={() => this.fuctionToBeCalled(true)} className="img-container float">
{child components....}
</a>
)
}
}
SomeComponent.test.js
import React from 'react';
import Enzyme, { shallow, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import SomeComponent from '../SomeComponent';
import SomeAnotherComp from "../SomeAnotherComp";
Enzyme.configure({ adapter: new Adapter() })
function setup() {
const props = {
openImagesDialog: false
}
let enzymeWrapper = shallow(<SomeComponent {...props} />)
return {
props,
enzymeWrapper
}
}
describe('components', () => {
const { props, enzymeWrapper } = setup()
it('should call fuctionToBeCalled(){}', () => {
const SomeAnotherCompProps = enzymeWrapper.find(SomeAnotherComp).props()
const fuctionToBeCalled = jest.fn(()=>true);
enzymeWrapper.find('a').simulate('click')
expect(fuctionToBeCalled).toBeCalled();
//expect(SomeAnotherCompProps.dialogOpen).toBe(true)
})
})
I'd like to know is there any other way to try this out.
Firstly, openImagesDialog is not a prop but a state in the component.
Secondly, fuctionToBeCalled is a function defined on component instance and you need spy on it instead of just creating a mock function . In order to do that you use spyOn on component instance. You can also check the state after simulating click
import React from 'react'
import Enzyme, { shallow, mount } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
import SomeComponent from '../SomeComponent'
import SomeAnotherComp from "../SomeAnotherComp";
Enzyme.configure({ adapter: new Adapter() })
function setup() {
const props = {
openImagesDialog: false
}
let enzymeWrapper = shallow(<SomeComponent {...props} />)
return {
props,
enzymeWrapper,
}
}
describe('components', () => {
const { props, enzymeWrapper } = setup()
it('should call fuctionToBeCalled(){}', () => {
const SomeAnotherCompProps = enzymeWrapper.find(SomeAnotherComp).props()
const instance = enzymeWrapper.instance();
jest.spyOn(instance, 'fuctionToBeCalled');
enzymeWrapper.find('a').simulate('click')
expect(instance.fuctionToBeCalled).toBeCalled();
expect(enzymeWrapper.state('openImagesDialog')).toEqual(true);
})
})

Reactjs unit test

I have a LoginInfo component and under this component i am calling one more child component. I am trying to write unit test case for the components using jest,enzyme and react test utils. partially i have wrote the test cases but not sure how i can write test for child component (LoginInfoEdit). that line i am not able to cover.
import React from 'react';
import { LoginInfoEdit } from './LoginInfoEdit'
class LoginInfo extends React.Component {
constructor(props) {
super(props)
this.state = {
isLoginInfo: false
}
}
openEdit() {
this.setState({ isLoginInfo: true })
}
closeEdit() {
this.setState({ isLoginInfo: false })
}
OpenEditForUpdate(e) {
e.preventDefault();
this.openEdit();
}
render() {
return (
<div>
<form>
<div>
some text
</div>
<LoginInfoEdit loginid={this.props.loginid} openloginedit={this.state.isLoginInfo} onClose={this.closeEdit.bind(this)}>
</LoginInfoEdit>
</form>
</div>
)
}
}
export default LoginInfo
Unit test is Below--------
import React from 'react'
import { shallow } from 'enzyme';
import LoginInfo from './LoginInfo'
import LoginInfoEdit from './LoginInfoEdit'
const props = {
loginid: "1",
openloginedit: false,
};
describe('LoginInfo component', () => {
let LoginInfo = null;
let editButton = null;
beforeEach(() => {
LoginInfo = shallow(<LoginInfo {...props}/>);
editButton = LoginInfo.find('button[name="edit"]')
})
it('checks everything set properly', () => {
editButton.simulate('click', { preventDefault: () => { } });
expect(LoginInfo.state('isloginedit')).toEqual(true)
})
it('renders child', () => {
expect(LoginInfo.find('LoginInfoEdit').length).toEqual(1)
});
it('passes proper props to the child', () => {
const expected = {
loginid: "1",
openloginedit: false,
onClose: LoginInfo.instance().closeEdit.bind(this),
};
expect(LoginInfo.find('LoginInfoEdit').props()).toEqual(expected)
})
})
Usually in such cases I care only about checking whether we render the child and pass props we want to the child like:
let component;
const props = someProps;
beforeEach(() => { component = mount(<LoginInfo { ..props } />); });
it('renders child', () => {
expect(component.find('LoginInfoEdit').length).to.eql(1)
});
it('passes proper props to the child', () => {
const expected = {
loginid: someVal,
openloginedit: someotherVal,
onClose: component.instance().closeEdit,
};
expect(component.find('LoginInfoEdit').props()).to.eql(expected)
});
and then I just test the children (in this case, LoginInfoEdit) separately from the parent

How to test react components with mobx observable state

Here is a simplified version of my component:
import React from 'react';
import { observable, action } from 'mobx';
import { observer } from 'mobx-react';
import { fromPromise } from 'mobx-utils';
#observer
export class MyComponent extends React.Component {
#action componentDidMount() {
const { store, params } = this.props;
this.warehouse = store.findById(params.id);
}
#observable warehouse = fromPromise(Promise.resolve());
render() {
return this.warehouse.case({
fulfilled: (value) => (
<div>
fulfilled
</div>
),
rejected: (error) => (
<div>
rejected
</div>
),
pending: () => (
<div>
pending
</div>
)
});
}
}
And here is my test (using jest and enzyme):
import React from 'react';
import { mount } from 'enzyme';
import toJson from 'enzyme-to-json';
import { observable, when } from 'mobx';
import { fromPromise } from 'mobx-utils';
import { MyComponent } from './MyComponent';
describe('<MyComponent>', () => {
it('should render correctly for state "fulfilled"', (done) => {
const mockStore = observable({
findById: jest.fn(() => fromPromise(Promise.resolve({ id: 'id' })))
});
const wrapper = mount(<MyComponent store={mockStore} params={{ id: '1' }} />);
const wh = wrapper.instance().warehouse;
when(
() => wh.state === 'fulfilled',
() => {
expect(wrapper.text()).toBe('fulfilled');
done();
}
);
});
});
The problem is that the handler for when in test runs before render method, so I don't have access to rendered markup there.
My question is how to run my except codes after rendering the fulfilled state.
Also I don't want to hack my component. Here I am using wrapper.instance().warehouse which I don't like very much.
Generally, the question would be how to test components with observable states in them?
I ended up with this solution:
import React from 'react';
import { mount } from 'enzyme';
import toJson from 'enzyme-to-json';
import { observable, when } from 'mobx';
import { fromPromise } from 'mobx-utils';
import { MyComponent } from './MyComponent';
describe('<MyComponent>', () => {
it('should render correctly for state "fulfilled"', (done) => {
const mockStore = observable({
findById: jest.fn(() => fromPromise(Promise.resolve({ id: 'id' })))
});
const wrapper = mount(<MyComponent store={mockStore} params={{ id: '1' }} />);
const wh = wrapper.instance().warehouse;
when(
() => wh.state === 'fulfilled',
() => {
process.nextTick(() => {
expect(wrapper.text()).toBe('fulfilled');
done();
});
}
);
});
});
Also there is a related question in mobx-react project issues.

Resources