I keep having a TypeError: Network request failed when I try to test a Snapshot of a component
here is the component
import {GetAllUsersPost} from './postdata';
class ManageUsers extends React.Component{
render(){
return(
{...}
);
}
componentDidMount(){
GetAllUsersPost(UserProfile.getId()).then((result) => {
this.setState({
parsed:result,
loading:false
})
});
}
}
Here is postdata
export function GetAllUsersPost(id) {
const json = JSON.stringify({id: id})
return new Promise((resolve, reject) => {
fetch(BaseURL + 'allusers', BdRequest(json)).then((response) => response.json()).then((res) => {
resolve(res);
}).catch((error) => {
reject(error);
});
});
}
And here is the test file (\src__tests__\ManageUsers.test.jsx)
import React from 'react';
import ManageUsers from '../component/ManageUsers';
import Adapter from 'enzyme-adapter-react-16';
import Enzyme, {shallow,mount} from 'enzyme';
Enzyme.configure({adapter:new Adapter()});
describe("ManageUsers", ()=>{
const wrapper = shallow(<ManageUsers/>);
const instance = wrapper.instance();
let response;
test("loading()",()=>{
wrapper.setState({loading:false})
response = JSON.stringify("")
expect(JSON.stringify(instance.loading())).toBe(response);
})
});
I know that my error is because of the promise (when Enzyme tries to shallow the component) but I can't make it to work...
thanks
Your test has to be set up as an async test. e.g.
it('should do something', async () => {
const result = await myAsyncMethod();
});
Edited for clarity - note that this is clearly untested, but what you need to look for is something from the render method and state, since that's all you do with the results.
import React from 'react';
import ManageUsers from '../component/ManageUsers';
import Adapter from 'enzyme-adapter-react-16';
import Enzyme, {shallow,mount} from 'enzyme';
Enzyme.configure({adapter:new Adapter()});
describe("ManageUsers", () => {
const wrapper = shallow(<ManageUsers/>);
test("loading()", async () => {
// wrapper.setState({loading:false}) // This should be a default
expect(wrapper.find('something from the render'));
expect(wrapper.state.parsedResults).toEqual('some result')
});
});
Related
enter image description hereI am trying to test a button in a component.
..........
like so
.........
import React, { Children } from "react";
import Enzyme, { shallow, mount, render } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import axios from "axios.js";
import data from "./mockData";
import "#testing-library/jest-dom";
import SegmentationLanding from "./SegmentationLanding";
import { BrowserRouter } from "react-router-dom";
Enzyme.configure({ adapter: new Adapter() });
jest.mock("axios.js");
const flushPromises = () => {
return new Promise((resolve) => setImmediate(resolve));
};
describe(`The segmentation lists`, () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<SegmentationLanding />);
});
test("the total number of records", async () => {
await flushPromises();
const w1 = mount(
<BrowserRouter>
<SegmentationLanding />
</BrowserRouter>
);
const btn = getByTestId("jest-tester");
expect(btn).toBeDisabled();
});
});
When i try to test, i get the error cannot read "down" property of undefined. Can anyone please suggest where i went wrong? I just wrote a simple test to test the button is disabled or not.
I am doing Unit tests with jest and enzyme. I have following connected component with hooks.
I called redux actions to load data.
import React, {useEffect, useState, useCallBack} from "react";
import {connect} from "react-redux";
import CustomComponent from "../Folder";
import { loadData, createData, updateData } from "../../redux/actions";
const AccountComponent = (props) => {
const total = 50;
const [aIndex, setAIndex] = useState(1);
const [arr, setArr] = useState(['ds,dsf']);
//... some state variables here
const getData = () => {
props.loadData(aIndex, total, arr);
}
useEffect(() => {
getData();
},[aIndex, total])
//some other useEffect and useCallback
return(
<React.Fragment>
<CustomComponent {...someParam}/>
<div>
...
</div>
</React.Fragment>
)
}
const mapStateToProps = (state) => {
const { param1, param2, parma3 } = state.AccountData;
return {
param1,
param2,
parma3
}
}
export default connect(mapStateToProps, { loadData, createData, updateData })(AccountComponent)
Here, like following I created some test case for above component.
import AccountComponent from "../";
import React from "react";
import renderer from "react-test-renderer"
describe("AccountComponent component", () => {
const loadData = jest.fn();
let wrapper;
it("snapshot testing", () => {
const tree = renderer.create(<AccountComponent loadData={loadData} />).toJSON();
expect(tree).toMatchSnapshot();
})
beforeEach(() => {
wrapper = shallow(<AccountComponent loadData={loadData} />).instance();
});
it('should call loadData', () => {
expect(wrapper.loadData).toHaveBeenCalled();
});
})
But, It doesn't pass and shows error.
Error for snapshot testing:
invariant violation element type is invalid: expected string or a class/function
Error for method call testing:
Cannot read property 'loadData' of undefined.
Enzyme Internal error: Enzyme expects and adapter to be configured, but found none. ...
Not sure what the issue as I am not good in unit testing.
I am using react-redux 7.
Any help would be greatly appreciated.
Edit:
I also tried with provider like following. But, didn't help.
import { Provider } from "react-redux";
import {createStore} from "redux";
import reducer from "../../reducers";
const store = createStore(reducer);
it("snapshot testing", () => {
const tree = renderer.create(<Provider store={store}><AccountComponent loadData={loadData} /></Provider>).toJSON();
expect(tree).toMatchSnapshot();
})
beforeEach(() => {
wrapper = shallow(<Provider store={store}><AccountComponent loadData={loadData} /></Provider>).instance();
});
In your case when you are using connected components in the same file you need to pass the state through Provider. Also, you need to configure your enzyme. And finally, when you are using react hooks, you will need to do asynchronous unit tests, because effects are async. When you are trying to check if any function has been called you need to "spy" on it.
import configureStore from 'redux-mock-store';
import React from 'react';
import renderer from 'react-test-renderer';
import Enzyme, { shallow } from 'enzyme';
import { Provider } from 'react-redux';
import Adapter from 'enzyme-adapter-react-16';
import { act } from 'react-dom/test-utils';
import createSagaMiddleware from 'redux-saga';
import AccountComponent from '../AccountComponent';
import * as actions from '../../../redux/actions';
jest.mock('../../../redux/actions', () => ({
loadData: jest.fn(),
createData: jest.fn(),
updateData: jest.fn(),
}));
const loadData = jest.spyOn(actions, 'loadData');
// configure Enzyme
Enzyme.configure({ adapter: new Adapter() });
const configureMockStore = configureStore([createSagaMiddleware]);
const initialState = {
AccountData: {
param1: 'param1',
param2: 'param2',
parma3: 'parma3 ',
},
};
const store = configureMockStore(initialState);
describe('AccountComponent component', () => {
let wrapper;
it('snapshot testing', () => {
const tree = renderer
.create(
<Provider store={store}>
<AccountComponent />
</Provider>,
)
.toJSON();
expect(tree).toMatchSnapshot();
});
beforeEach(async () => {
await act(async () => {
wrapper = shallow(
<Provider store={store}>
<AccountComponent />
</Provider>,
);
});
await act(async () => {
wrapper.update();
});
});
it('should call loadData', () => {
expect(loadData).toHaveBeenCalled();
});
});
Please mock your AccountData state with properties which will be used in that component. Also, I am not sure where is your test file is located, so you might need to change import path from '../../../redux/actions' to you actions file path. Finally, I am not sure what middleware you are using, so fill free to replace import createSagaMiddleware from 'redux-saga'; with your middleware for redux.
If you are using react it already comes with #testing-library and you don't need enzyme to do snapshot testing. This is how I do my snapshot testing.
import React, { Suspense } from "react";
import { screen } from "#testing-library/react";
import "#testing-library/jest-dom/extend-expect";
import AccountComponent from "../";
import store from "./store";// you can mock the store if your prefer too
describe("<AccountComponent />", () => {
test("it should match snapshot", async () => {
expect.assertions(1);
const { asFragment } = await render(
<Suspense fallback="Test Loading ...">
<Provider store={store}>
<AccountComponent />
</Provider>
</Suspense>
);
expect(asFragment()).toMatchSnapshot();
});
});
When it is a functional component and you are using hooks, unit tests may not work with shallow rendering. You have to use 'renderHooks' instead to create a wrapper. Please refer https://react-hooks-testing-library.com/ for more details.
I'm trying to provide a mock request for this class and then expect that history.push is called with some path.
Start.js
import React from 'react'
import { useHistory } from 'react-router-dom';
import axios from 'axios';
import { ReactComponent as Arrow } from '../../arrow.svg';
export default function Start() {
let history = useHistory();
const doInitializeApp = () => {
axios.get('http://localhost:8080/api/v1/asap/start')
.then(res => {
if (res.data == true) {
history.push('/login')
} else {
alert('something went wrong. Could not start the application')
}
}).catch(err => {
alert('something went wrong. Could not contact the server!')
});
}
return (
<div>
<div className="container">
<div className="content">
<div id="box">
<h1>Welcome</h1>
<Arrow id="next" onClick={doInitializeApp} />
</div>
</div>
</div>
</div>
);
}
And this is my approach for the test
Start.test.js
import React from 'react';
import Enzyme, { shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import Start from '../components/startscreen/Start';
import { ReactComponent as Arrow } from '../arrow.svg';
import axios from "axios";
Enzyme.configure({ adapter: new Adapter() });
describe('Start', () => {
it('test axios get reroute the application to path /login', () => {
const mProps = { history: { push: jest.fn() } };
const wrapper = shallow(<Start {...mProps} />);
const arrow = wrapper.find(Arrow);
const axiosSpy = jest.spyOn(axios, 'get');
//mock axios
jest.mock("axios");
//mock axios response
axios.get.mockResolvedValue({ data: true });
//simulate onclick
arrow.simulate('click');
expect(axiosSpy).toHaveBeenCalled(); --> this pass
expect(mProps.history.push).toBeCalledWith('/login'); --> this doesn't pass
})
});
However, the test did not pass because the actual axios.get(url) doesn't take the response which I mocked and it always come to the .catch(err => ... "Could not contact the server!")
What did I do wrong in here ? Because that the code didn't come to the if (res.data===true) so that I also couldn't test whether the history.push is actually called or not.
Your mocking code is fine. The code in the catch block is getting executed since useHistory() returns undefined (You can confirm this by console.logging the error inside the catch block).
One way to fix it would be to mock useHistory and pass a mock function for history.push. You can then spy on useHistory() to confirm the history.push got called with /login.
import { useHistory } from 'react-router-dom'
// other import statements omitted for brevity
jest.mock('axios')
jest.mock('react-router-dom', () => {
const fakeHistory = {
push: jest.fn()
}
return {
...jest.requireActual('react-router-dom'),
useHistory: () => fakeHistory
}
})
const flushPromises = () => new Promise(setImmediate)
describe('Start component', () => {
test('redirects to /login', async () => {
const pushSpy = jest.spyOn(useHistory(), 'push')
axios.get.mockResolvedValue({ data: true })
const wrapper = shallow(<App />)
const button = wrapper.find(Arrow)
button.simulate('click')
await flushPromises()
expect(pushSpy).toBeCalledWith('/login')
})
})
I'm using setImmediate to wait for the async action to complete as suggested here.
I am writing unit tests and want to test state change callback from the component.
Unit Test code
import React from 'react';
import {configure} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import NameTextField from './NameTextField';
import TextField from '#material-ui/core/TextField';
import {createShallow} from '#material-ui/core/test-utils';
import {act} from 'react-dom/test-utils';
configure({adapter: new Adapter()});
describe('<NameTextField />', () => {
let shallow;
beforeAll(() => {
shallow = createShallow();
});
let wrapper;
beforeEach(() => {
wrapper = shallow(<NameTextField onStateChange={handleStateChange}/>);
});
const handleStateChange = updatedState => {
};
it('should show no error when correctly entered', () => {
const handleStateChange = jest.fn()
act(() => {
wrapper.find(TextField).at(0).simulate('blur', {target: {value: 'correct name'}});
});
wrapper.update();
expect(wrapper.find(TextField).at(0).props().error).toBe(
false);
expect(wrapper.find(TextField).at(0).props().helperText).toBe(
null);
expect(handleStateChange).toHaveBeenCalledWith('10')
});
});
I have NameTextField component here where depending on it's input on Textfield, I get onStateChange callback.
<NameTextField onStateChange={handleStateChange}/>
When I test using
expect(handleStateChange).toHaveBeenCalledWith('10')
I get error message saying
Error: expect(jest.fn()).toHaveBeenCalledWith(expected)
Expected mock function to have been called with:
["10"]
But it was not called.
How would I capture the stateChange callback on the component?
Found the answer as soon as I posted here.
Simple mistake.
import React from 'react';
import {configure} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import NameTextField from './NameTextField';
import TextField from '#material-ui/core/TextField';
import {createShallow} from '#material-ui/core/test-utils';
import {act} from 'react-dom/test-utils';
configure({adapter: new Adapter()});
describe('<NameTextField />', () => {
let shallow;
beforeAll(() => {
shallow = createShallow();
});
let wrapper;
beforeEach(() => {
wrapper = shallow(<NameTextField onStateChange={handleStateChange}/>);
});
const handleStateChange = jest.fn();
it('should show no error when correctly entered', () => {
act(() => {
wrapper.find(TextField).at(0).simulate('blur', {target: {value: 'correct name'}});
});
wrapper.update();
expect(wrapper.find(TextField).at(0).props().error).toBe(
false);
expect(wrapper.find(TextField).at(0).props().helperText).toBe(
null);
expect(handleStateChange).toHaveBeenCalledWith('10')
});
});
const handleStateChange = jest.fn();
there was duplicate handleStateChange.
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);
})
})