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
Related
File-
import React from "react";
import PropTypes from "prop-types";
import * as css from "#/utils/css";
class CheckFile extends React.Component {
static propTypes = {
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
timeout: PropTypes.number
};
state = {
active: false
};
componentDidUpdate(oldProps) {
//not receiving props
}
componentWillUnmount() {
window.clearTimeout(this.timeoutEl);
}
render() {
return (
<div>
</div>
);
}
}
export default CheckFile;
Test File:
import React from "react";
import renderer from "react-test-renderer";
import { render } from "#testing-library/react";
import CheckFile from "../index";
describe("Test for PullToRefresh", () => {
it("check for snapshot", () => {
const Comp = () => <CheckFile />;
const props1 = { id: "test-id1", timeout: 1000, text: "testing snackbar1" };
const component = renderer.create(<Comp {...props1} />);
const { rerender } = render(<Comp />);
const props2 = {
id: "test-id2",
timeout: 1000,
text: "testing snackbar1"
};
rerender(<Comp {...props2} />);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
});
When I send Props from test file it is not received by class component.Showing undefined .
When I send Props from test file it is not received by class component.Showing undefined .
When I send Props from test file it is not received by class component.Showing undefined .
I don't understand why you are trying to test CheckFile but then render it in a locally declared component and then don't pass any props through to it.
describe("Test for PullToRefresh", () => {
it("check for snapshot", () => {
const Comp = () => <CheckFile />; // <-- no props passed here!!
const props1 = { id: "test-id1", timeout: 1000, text: "testing snackbar1" };
const component = renderer.create(<Comp {...props1} />);
const { rerender } = render(<Comp />);
const props2 = {
id: "test-id2",
timeout: 1000,
text: "testing snackbar1"
};
rerender(<Comp {...props2} />);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
});
Either proxy the props through to CheckFile:
const Comp = (props) => <CheckFile {...props} />;
Or just use CheckFile directly in test:
describe("Test for PullToRefresh", () => {
it("check for snapshot", () => {
const props1 = { id: "test-id1", timeout: 1000, text: "testing snackbar1" };
const component = renderer.create(<CheckFile {...props1} />);
const { rerender } = render(<CheckFile />);
const props2 = {
id: "test-id2",
timeout: 1000,
text: "testing snackbar1"
};
rerender(<CheckFile {...props2} />);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
});
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);
});
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);
});
});
I have a react component that renders conditionally (renders if data is fetched otherwise returns null) and I want to test this with jest & enzyme. The problem that I'm having is I want to test one of the methods in the class but .instance() keeps returning null so it doesn't allow me to test the instance.
my code looks something like this
export default class MyComponent extends React.Component<Props, State> {
componentDidMount() {
this.props.fetchData.then(() =>
this.setState({ loaded: true });
);
}
methodThatIWantToTest() {
//do some stuff here
}
render() {
if (this.loaded) {
// render stuff here
} else {
return null;
}
}
}
and in the test I want to test
describe('myComponent', () => {
it('should do some stuff', () => {
const shallowWrapper = shallow(<MyComponent {...props}/>);
const method = shallowWrapper.instance().methodThatIWantToTest();
....such and such
});
});
but it looks like MyComponent only returns null so shallowWrapper.instance() returns null as well. I tried shallowWrapper.update() and many other things but it seems it doesn't want to render at all.. How do I wait for my component to be updated and then starts expect statement?
has anyone had a similar issue as mine and know how to work around this?
It is render result and not an instance that is null. shallowWrapper.instance() is an instance of component class, it cannot be null for stateful component. As the reference states:
Returns (React 16.x)
ReactComponent: The stateful React component instance.
null: If stateless React component was wrapped.
While shallowWrapper.html() will be initially null indeed.
There is a mistake in original code, it should be this.state.loaded and not this.loaded:
MyComponent extends React.Component {
state = { loaded: false };
componentDidMount() {
this.props.fetchData.then(() => {
this.setState({ loaded: true });
});
}
methodThatIWantToTest() {
//do some stuff here
}
render() {
if (this.state.loaded) {
return <p>hi</p>;
} else {
return null;
}
}
}
componentDidMount and methodThatIWantToTest should be preferably considered different units. They belong to different tests. In case methodThatIWantToTest is called in lifecycle hooks, it may be stubbed in componentDidMount test:
it('should fetch data', async () => {
const props = { fetchData: Promise.resolve('data') };
const shallowWrapper = shallow(<MyComponent {...props}/>);
expect(shallowWrapper.html()).toBe(null);
await props.fetchData;
expect(shallowWrapper.html()).toBe('<p>hi</p>');
});
Then the method can be tested separately. Lifecycle hooks can be disabled to reduce number of moving parts:
it('should do some stuff', () => {
const shallowWrapper = shallow(<MyComponent {...props}/>, {disableLifecycleMethods: true});
const result = shallowWrapper.instance().methodThatIWantToTest();
expect(result).toBe(...);
});
Here is a working example:
myComponent.js
import * as React from 'react';
export default class MyComponent extends React.Component {
constructor(...props) {
super(...props);
this.state = { loaded: false };
}
componentDidMount() {
this.props.fetchData().then(() =>
this.setState({ loaded: true })
);
}
methodThatIWantToTest() {
return 'result';
}
render() {
if (this.state.loaded) {
return <div>loaded</div>;
} else {
return null;
}
}
}
myComponent.test.js
import * as React from 'react';
import { shallow } from 'enzyme';
import MyComponent from './myComponent';
describe('myComponent', () => {
it('should do some stuff', async () => {
const fetchData = jest.fn(() => Promise.resolve());
const props = { fetchData };
const shallowWrapper = shallow(<MyComponent {...props}/>);
expect(shallowWrapper.html()).toBe(null);
expect(shallowWrapper.instance().methodThatIWantToTest()).toBe('result');
// pause the test and let the event loop cycle so the callback
// queued by then() within componentDidMount can run
await Promise.resolve();
expect(shallowWrapper.html()).toBe('<div>loaded</div>');
});
});
It seems like you can't test state in react components once you make them connected components. Anyone know why? To illustrate the point, I have a test for a react component that passes without redux and fails as soon as you make it connected.
// MyComponent.jsx
import React from 'react'
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
foo: 'bar'
};
}
render() {
return <div></div>
}
}
export default MyComponent
Here is the passing test:
// MyComponent.test.js
import React from 'react'
import MyComponent from '../src/components/MyComponent'
import { mount } from 'enzyme'
describe('MyComponent', () => {
describe('interactions', () => {
let wrapper
beforeEach(() => {
wrapper = shallow(<MyComponent />)
})
it('foo to equal bar', () => {
expect(wrapper.state().foo).toEqual('bar')
})
})
})
Now I'm introducing Redux and connecting the component:
// MyComponent.jsx
...
export default connect(function (state){
return {
currentUser: state.currentUser
}
})(MyComponent);
And here is the updated test:
// MyComponent.test.js
import React from 'react'
import MyComponent from '../src/components/MyComponent'
import { mount } from 'enzyme'
import configureStore from 'redux-mock-store'
describe('MyComponent', () => {
const state = {}
const mockStore = configureStore()
describe('interactions', () => {
let wrapper
beforeEach(() => {
wrapper = mount(<MyComponent store={ mockStore(state) } />)
})
it('foo to equal bar', () => {
expect(wrapper.state().foo).toEqual('bar')
})
})
})
I would recommend just exporting both the connect() (the default export) and the actual component itself.
This way you can test the component separately outside of the connected version of the component.
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
foo: 'bar'
};
}
render() {
return <div></div>
}
}
export MyComponent
export default connect(function (state){
return {
currentUser: state.currentUser
}
})(MyComponent);
and then the test:
import { MyComponent } from '../src/components/MyComponent'
import { mount } from 'enzyme'
describe('MyComponent', () => {
describe('interactions', () => {
let wrapper
beforeEach(() => {
wrapper = shallow(<MyComponent />)
})
it('foo to equal bar', () => {
expect(wrapper.state().foo).toEqual('bar')
})
})
})
That's because connect() generates a wrapper component that manages the store interaction process. In your second snippet, <MyComponent> is now the component generated by connect(), not your original component. You would need to dig another level of nesting deeper in the render hierarchy to check on the contents of the real component's state.