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.
Related
Can someone help me cover this test case, I am not able to figure out how to cover this inline function
Note: DropdownField is a wrapper component and contains the actual which is imported from
import { Field } from "redux-form";
dropdown input inside
I have tried to call mockfunction and jest.fn() but nothing works, Any help will be appreciated because I am totally blank at the moment. Thanks in advance to all the wonderful devs
import React from "react";
import DropdownField from "components/FormFields/DropdownField";
import get from "lodash/get";
const AddressLookup = props => {
const {
change,
formValues,
fetchAddressLookup,
postalCodeOptions,
type = "delivery",
placeholder = "type_to_search",
field
} = props;
const selectedDeliveryMethod = get(formValues, "delivery_method", {});
return (
<DropdownField
placeholder={placeholder}
options={postalCodeOptions}
{...selectedDeliveryMethod.fields.zip_code}
isSearchable={true}
field={field}
onInputChange={value => {
if (value.length >= 2) fetchAddressLookup({ q: value });
}}
onChange={({ value }) => {
const [city, state, zipCode] = value.split("-");
change(field, value);
change(`${type}_state`, state);
change(`${type}_city`, city);
change(`${type}_zip_code`, zipCode);
}}
/>
);
};
export default AddressLookup;
I have tried this approach but It failed to cover. First test case covers the UI part only as you can see it is matching to snapshot. In second test cases I removed some code and commented some because nothing works
import * as React from 'react';
import { render, fireEvent, wait } from '#testing-library/react';
import { IntlProvider } from 'react-intl';
import { Provider } from 'react-redux';
import { reduxForm } from 'redux-form';
import configureStore from 'redux-mock-store';
import messages from '__fixtures__/messages.json';
import AddressLookup from '../index';
const DecoratedAddressLookup = reduxForm({ form: 'testForm' })(AddressLookup);
const testProps = {
change: jest.fn(),
fetchAddressLookup: jest.fn(),
postalCodeOptions: [
{
name: 'abc-abcd-1234',
value: 'abc-abcd-1234',
},
],
formValues: {
delivery_method: {
fields: {
zip_code: 'BD-BDBD-1234',
},
},
},
field: 'zip_code',
};
describe('<AddressLookup />', () => {
let testStore;
let addressField;
const mockStore = configureStore([]);
const store = mockStore({});
const spy = jest.fn();
beforeAll(() => {
testStore = store;
});
const renderComponent = () => {
return render(
<Provider store={testStore}>
<IntlProvider locale='en' messages={messages}>
<DecoratedAddressLookup
{...testProps}
onInputChange={spy}
onChange={spy}
/>
</IntlProvider>
</Provider>
);
};
it('should render and match the snapshot', () => {
const {
getByTestId,
container: { firstChild },
} = renderComponent();
addressField = getByTestId('zip_code');
expect(firstChild).toMatchSnapshot();
});
it('should type a value', async () => {
addressField = addressField.querySelector('input');
// expect(addressField).toBeTruthy();
// console.log('addressField', addressField);
// const input = screen.getByTestId('add-word-input');
fireEvent.change(addressField, { target: { value: 'abc-abcd-1234' } });
expect(addressField).toHaveValue('abc-abcd-1234');
// expect(testProps.change).toBeCalled();
await wait(() => {
expect(spy).toHaveBeenCalledTimes(1);
});
});
});
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
I am trying to perform some unit testing on my existing react application with Jest and Enzyme. I am totally new to this stuff and accurately I do not know how to approach such test scenarios. I know that to test API request calls I have to perform some "mocking", but how should I write the test for that?. What will be the steps that needs to be followed?
Following is the code snippet I am looking to test.
Home.js
import React,{Component} from 'react'
import axios from 'axios';
import {Link} from 'react-router-dom';
import FacilityModal from '../Table/FacilityModal';
class Home extends Component {
state = {
cities:[],
name:''
}
componentDidMount() {
axios.get('/cities').then(res => {
this.setState({cities:res.data})
console.log("Oza" + JSON.stringify(res))
});
console.log(this.state.cities)
}
render() {
let postList = this.state.cities.map(city => {
return(
<div key = {city.id}>
<p>
<Link to = {'/'+city.id}>{city.name}</Link>
</p>
</div>
)
})
return(
<div className = 'align'>All Facilities (NCAL)
<div className="hr-sect">OR</div>
<div className = 'Modal'>
{postList}
</div>
<FacilityModal cityname = {this.props} />
</div>
)
}
}
import React from 'react';
import axios from 'axios';
export default class ArticleList extends React.Component {
constructor(props) {
super(props);
this.state = {
articles: []
}
}
componentDidMount() {
return axios.get('GET_ARTICLES_URL').then(response => {
this.setState({
articles: response.data
});
});
}
render() {
return (
<ul>
{this.state.articles.map(a => <li><a href={a.url}>{a.title}</a></li>)}
</ul>
)
}
}
// ---------
import React from 'react';
import { shallow } from 'enzyme';
import App from './App';
jest.mock('axios', () => {
const exampleArticles = [
{ title: 'test article', url: 'test url' }
];
return {
get: jest.fn(() => Promise.resolve(exampleArticles)),
};
});
const axios = require('axios');
it('fetch articles on #componentDidMount', () => {
const app = shallow(<App />);
app
.instance()
.componentDidMount()
.then(() => {
expect(axios.get).toHaveBeenCalled();
expect(axios.get).toHaveBeenCalledWith('articles_url');
expect(app.state()).toHaveProperty('articles', [
{ title: 'test article', url: 'test url' }
]);
done();
});
});
1) Extract the API call in another method that returns the promise(for eg: fetchCities()) for ease of testing.
2) Mock the axios node module with Jest. Refer docs: https://jestjs.io/docs/en/mock-functions#mocking-modules
3) Use Enzyme to get a reference to your component: https://airbnb.io/enzyme/docs/api/ShallowWrapper/shallow.html
Once that's in place, you can verify if the state is set correctly. For eg:
test('should fetch users', () => {
const wrapper = shallow(<Home/>);
const resp = {data: [{cities: ['NY']}]};
axios.get.mockResolvedValue(resp);
wrapper.instance().fetchCities().then(resp => {
expect(wrapper.state('cities')).toEqual(resp.data.cities);
});
});
How do i improve this answer? It is not what i am expecting as response which is name of the cities.
axios.js (seperate function for promise)
'use strict';
module.exports = {
get: () => {
return Promise.resolve({
data: [
{
id: 0,
name: 'Santa Clara'
},
{
id: 1,
name: 'Fremont'
}
]
});
}
};
Home.test.js (actual test file)
import React from 'react';
import { shallow,configure } from 'enzyme';
import Home from './Home';
import axios from 'axios';
import Adapter from 'enzyme-adapter-react-16';
configure({adapter:new Adapter()});
jest.mock('axios');
describe('Home component', () => {
describe('when rendered', () => {
it('should fetch a list of cities', () => {
const getSpy = jest.spyOn(axios, 'get');
const cityInstance = shallow(
<Home/>
);
expect(getSpy).toBeCalled();
});
});
});
I have a component that extends a generic component (custom react component). How do I unit-test using enzyme's mount? Here's the code:
Container.js
import React from 'react';
import request from 'API/request';
export default class Container extends React.Component {
componentDidMount() {
this.populateData();
}
componentDidUpdate() {
if (this.isDataStale()) {
return this.populateData();
}
}
populateData() {
const url = this.getUrl();
const params = this.getParams();
return request(this.props.dispatch, {
url,
params,
method: 'GET'
})
.then(response => {
this.dispatchData(response.data);
})
.catch(error => {
this.dispatchError(error);
});
}
render() {
if (this.getData() && !this.isDataStale()) {
return this.renderChildren();
} else {
return null;
}
}
getError() {}
getParams() {}
isDataStale() {
return false;
}
}
Here's the functional component:
import React from 'react';
import { connect } from 'react-redux';
import { Route, withRouter } from 'react-router-dom';
import CustomPage from 'Client/customPage';
import Container from 'Client/container';
import TestPanel from 'Client/testPanel/TestPanel';
import Greeting from 'Client/greeting';
import Button from '../Button';
export class Test extends Container {
constructor({ match }) {
super();
this.state = {
match: match,
id: match.params.id,
location: ''
};
}
isDataStale() {
return (
this.props.data.id !== this.props.match.params.id
);
}
getData() {
return this.props.data.values;
}
dispatchData(data) {
this.props.dispatch({
type: 'DATA_FOUND',
data: data,
id: this.state.id
});
}
dispatchError(error) {
this.props.dispatch({ type: 'CUSTOM_ERROR', data: error });
}
showDetails(url) {
this.props.history.push(url);
}
renderChildren() {
const match = this.state.match;
const testUrl = `/test/values/${this.state.id}`;
const verifyUrl = `${testUrl}/verify`;
return (
<div className="test-plan">
<Route path={verifyUrl} render={() => (
<Greeting show={true} />
)} />
<TestPanel
data={this.props.data}
id={this.props.match.params.id}
/>
<Route exact path={testUrl} render={() => (
<CustomPage id={this.state.id} />
)} />
<Route path={verifyUrl} render={() => (
<div className="next-button" ><Button label = {' NEXT '} onButtonClick = { this.showDetails.bind(this, loanUrl) } /> </div>
)} />
</div>
);
}
}
const mapStateToProps = state => {
return {
data: state.data
};
};
export default withRouter(connect(mapStateToProps)(Test));
How do I test this Test (functional) component using Enzyme? Am not sure how this mock works since the render method is inside the container which in turn invokes the renderChildren method in our functional component. Lot of functional components extends this container so we can't change this to composition pattern as of now.
Here's the test case (Not working)
import jsdomWindow from '../../test.setup';
import React from 'react';
import {BrowserRouter} from 'react-router-dom';
import { Test } from 'Client/containers/test';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import Enzyme, {mount} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import sinon from 'sinon';
import * as api from 'API/request';
import { populateData, isDataStale } from '../../../../src/client/app/container';
Enzyme.configure({ adapter: new Adapter() });
const sandbox = sinon.sandbox.create();
describe('<Test />',() => {
beforeEach(function() {
sandbox.stub(api, 'default').returns(Promise.resolve({data: { saySomething: 'Yay Tests!'}}));
});
afterEach(function() {
sandbox.restore();
});
const match = {
params: {
id: 100
}
};
const testState = {
data : {
test: {
id:'100',
email:'someone#something.com',
first_name:'John',
home_phone:'325-555-6564',
last_name:'Doe'
}
}
};
const wrapper = mount(
<BrowserRouter>
<Provider store={createStore(state => state, { data: testState.data })}>
<Test data={testState.data} dispatch={ sinon.spy() }
match={match} />
</Provider>
</BrowserRouter>
);
jest.mock('../../../../src/client/app/container', () => ({
populateData: jest.fn(),
isDataStale: jest.fn(),
getError: jest.fn(),
getParams: jest.fn(),
}));
it('should render', () => {
console.log(wrapper.debug());
populateData.mockReturnValueOnce({});
isDataStale.mockReturnValueOnce(false);
const greetingContainer = wrapper.find('.greeting-container').find('.quick-info').find('.quick-info-heading');
expect(greetingContainer.text()).toContain('PROPER RESPONSE');
});
// it('should render greeting component by default', () => {
// expect(wrapper.find('div.greeting-container')).toBeDefined();
// expect(wrapper.find('div.info-container')).toBeDefined();
// expect(wrapper.find('div.next-button')).toBeDefined();
// });
// it('should not render greeting and next-button components on clicking next', () => {
// console.log(`Test**** ${JSON.stringify(wrapper.find('div.plan'))}`);
// });
});
Could anyone help me to unit test this using mount method of enzyme. Don't want to use shallow here as i have to test nested components' properties.
Thank you.
I am just trying to figure out how to do tests on components that are wrapped with connect. How do I properly define the redux state prop to my component?
● PendingContract with connect/Redux › +++ render the connected(SMART) component
TypeError: Cannot read property 'find' of undefined
Original Component code:
// Dependencies
import React, { Component } from 'react';
import CSSModules from 'react-css-modules';
import { connect } from 'react-redux';
import * as actions from '../../../../actions';
import PendingContractDetail from './pending-contract-
detail/PendingContractDetail';
// CSS
import styles from './PendingContract.css';
export class PendingContract extends Component {
componentWillMount() {
this.props.getSinglePendingContract(this.props.params.contract);
}
render() {
let contract;
if (this.props.contract) {
const contractDetails = this.props.contract;
contract = (
<PendingContractDetail
accepted={contractDetails.accepted}
contractId={contractDetails.contractId}
contractName={contractDetails.contractName}
details={contractDetails.details}
status={contractDetails.status}
timeCreated={contractDetails.timeCreated}
type={contractDetails.type} />
);
} else {
contract = 'Loading...'
};
return (
<div className='row'>
<div className='col-xs-12 col-sm-12 col-md-12'>
{contract}
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
contract: state.pendingContracts.contract
}
}
const PendingContractWithCSS = CSSModules(PendingContract, styles);
export default connect(mapStateToProps, actions)(PendingContractWithCSS);
Test Code as follows:
import React from 'react';
import reduxThunk from 'redux-thunk';
import { Provider } from 'react-redux';
import { shallow, mount } from 'enzyme';
import PendingContract from './PendingContract';
import configureStore from 'redux-mock-store';
jest.mock('react-css-modules', () => Component => Component);
describe('PendingContract with connect/Redux', () => {
const initialState = {
contract: {
accepted: true,
contractId: 1234,
contractName: 'Test Contract',
details: { test: 'test'},
status: 'Accepted',
type: 'Sports'
}
};
const mockStore = configureStore([reduxThunk])
let store,wrapper;
beforeEach(()=>{
store = mockStore(initialState)
wrapper = mount(<Provider store={store}><PendingContract {...initialState} /></Provider>)
})
it('+++ render the connected(SMART) component', () => {
expect(wrapper.find(PendingContract).length).toEqual(1)
});
// it('+++ check Prop matches with initialState', () => {
// expect(wrapper.find(PendingContract).prop('contract')).toEqual(initialState.contract)
// });
});
You need to import connected component if you are trying to fully test it with mount:
import React from 'react';
import reduxThunk from 'redux-thunk';
import { Provider } from 'react-redux';
import { shallow, mount } from 'enzyme';
import ConnectedPendingContract, { PendingContract } from './PendingContract';
import configureStore from 'redux-mock-store';
jest.mock('react-css-modules', () => Component => Component);
describe('PendingContract with connect/Redux', () => {
const initialState = {
contract: {
accepted: true,
contractId: 1234,
contractName: 'Test Contract',
details: { test: 'test'},
status: 'Accepted',
type: 'Sports'
}
};
const mockStore = configureStore([reduxThunk])
let store,wrapper;
beforeEach(()=>{
store = mockStore(initialState)
wrapper = mount(<Provider store={store}><ConnectedPendingContract {...initialState} /></Provider>)
})
it('+++ render the connected(SMART) component', () => {
expect(wrapper.find(PendingContract).length).toEqual(1)
});
// it('+++ check Prop matches with initialState', () => {
// expect(wrapper.find(PendingContract).prop('contract')).toEqual(initialState.contract)
// });
});