I have the following component, using Flow:
//#flow
import React, { type Node } from 'react';
import { useIntl } from 'react-intl';
type Props = { balance: Object };
const AvailableDiscount = ({ balance }: Props): Node => {
const { formatMessage, locale } = useIntl();
return (
<div>
{formatMessage({ id: 'XAM_DISCOUNT_DETAILS' })}: {balance.value}
</>
);
};
And while testing it, I seem to have a problem when trying it so mount it with shallow, using Enzyme:
// #flow
import { mount, shallow } from 'enzyme';
import React from 'react';
import { IntlProvider } from 'react-intl';
import balance from '../../../utils/testHelpers/testData/customerBalance';
import AvailableDiscount from './AvailableDiscount';
describe('AvailableDiscount', () => {
it('renders correctly', () => {
const component = <AvailableDiscount balance={balance} />;
const wrappingOptions = {
wrappingComponent: IntlProvider,
wrappingComponentProps: {
locale: 'en',
defaultLocale: 'en',
messages: {},
},
};
const mountedComponent = mount(component, wrappingOptions); // <-- This works
const shallowComponent = shallow(component, wrappingOptions); // <-- This does NOT work
});
});
It tells me that the component does not seem to be wrapped in the provider.
While this seems to work for mount, shallow keeps giving me this error. Why could this be?
Related
Am new in writing testcases using React Test library.
Here is my component
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
class MyContainer extends React.Component {
static propTypes = {
graphicalData: PropTypes.object.isRequired,
};
render() {
const { graphicalData } = this.props;
return (
graphicalData && (
<div>
/////some action and rendering
</div>
))}
}
const mapStateToProps = (state) => {
return {
graphicalData: state.design.contents ? state.design.contents.graphicalData : {},
};
};
const mapDispatchToProps = (dispatch) => ({});
export default connect(mapStateToProps, mapDispatchToProps)(MyContainer)));
So i am writing my test case file using React Testing library
import React from 'react';
import '#testing-library/jest-dom';
import { render, cleanup, shallow } from '#testing-library/react';
import MyContainer from '../../MyContainer';
import configureMockStore from 'redux-mock-store';
import ReactDOM from 'react-dom';
const mockStore = configureMockStore();
import { Provider } from 'react-redux';
const store = mockStore({
state: {
design: {
contents: {
graphicalModel: { cars: [{},{}], bikes: [{},{}] },
},
},
},
});
afterEach(cleanup);
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(
<Provider store={store}>
<MyContainer />
</Provider>,
div
);
ReactDOM.unmountComponentAtNode(div);
});
Am not sure what went wrong , my idea is to check if the component loads without crashing , also if the cars array length is greater than 0 , check something rendered on page.
But am getting some error, any help with example or suggestion will save my day
The error seems correct. Check the structure that you have passed into the mockStore() function.
It is
state: {
design: {
contents: {
graphicalModel: { cars: [{},{}], bikes: [{},{}] },
},
},
},
So, when you access in your MyContainer, you should access it as
state.state.design.contents
It is just that you have an extra hierarchy in the state object.
Or, if you don't want to change the component, change the structure of the state passed into your mockStore() method as:
const store = mockStore({
design: {
contents: {
graphicalModel: { cars: [{},{}], bikes: [{},{}] },
},
},
});
Please help.
I cannot run test because in my util langProvider is package that drops tests.
I have to mock langProvider function or strings variable.
How I can do that?
import React, { ReactElement, useState } from 'react';
import langProvider from 'utils/langProvider';
import { HeaderStyled, DropDownStyled } from './styled';
import './style.scss';
function Header(props: any): ReactElement {
const [state, setState] = useState({
isDropDownOpened: false,
isDrawerOpened: false,
});
const strings = langProvider(props.language, 'components.header');
return (
<HeaderStyled className="header">
...
</HeaderStyled>
);
}
const mapStateToProps = (state: any) => ({
language: state.language,
});
export default connect(mapStateToProps)(Header);
My test file
import { configure, shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import React from 'react';
import Header from './index';
configure({ adapter: new Adapter() });
describe('<Header />', () => {
const wrapper = shallow(<Header />);
it('My test', () => {
expect('').toEqual('Header');
});
});
not sure, but you can try to insert in your test file before describe function, something like that:
jest.mock("utils/langProvider", () => ({langProvider: ()=> []}));
I'm having issues testing my components that use dispatch via useReducer with React-testing-library.
I created a less complex example to try to boil down what is going on and that is still having the same dispatch is not a function problem. When I run my tests, I am getting this error:
11 | data-testid="jared-test-button"
12 | onClick={() => {
> 13 | dispatch({ type: 'SWITCH' })
| ^
14 | }}
15 | >
16 | Click Me
Also, if I do a console.log(typeof dispatch) inside RandomButton, and I click on the button the output says function.
Here is the test in question.
import React from 'react'
import RandomButton from '../RandomButton'
import { render, fireEvent } from '#testing-library/react'
describe('Button Render', () => {
it('click button', () => {
const { getByTestId, queryByText } = render(<RandomButton />)
expect(getByTestId('jared-test-button')).toBeInTheDocument()
fireEvent.click(getByTestId('jared-test-button'))
expect(queryByText('My name is frog')).toBeInTheDocument()
})
})
Here is my relevant code:
RandomButton.js
import React, { useContext } from 'react'
import MyContext from 'contexts/MyContext'
const RandomButton = () => {
const { dispatch } = useContext(MyContext)
return (
<div>
<Button
data-testid="jared-test-button"
onClick={() => {
dispatch({ type: 'SWITCH' })
}}
>
Click Me
</Button>
</div>
)
}
export default RandomButton
MyApp.js
import React, { useReducer } from 'react'
import {myreducer} from './MyFunctions'
import MyContext from 'contexts/MyContext'
import RandomButton from './RandomButton'
const initialState = {
blue: false,
}
const [{ blue },dispatch] = useReducer(myreducer, initialState)
return (
<MyContext.Provider value={{ dispatch }}>
<div>
{blue && <div>My name is frog</div>}
<RandomButton />
</div>
</MyContext.Provider>
)
export default MyApp
MyFunctions.js
export const myreducer = (state, action) => {
switch (action.type) {
case 'SWITCH':
return { ...state, blue: !state.blue }
default:
return state
}
}
MyContext.js
import React from 'react'
const MyContext = React.createContext({})
export default MyContext
It is probably something stupid that I am missing, but after reading the docs and looking at other examples online I'm not seeing the solution.
I've not tested redux hooks with react-testing-library, but I do know you'll have to provide a wrapper to the render function that provides the Provider with dispatch function.
Here's an example I use to test components connected to a redux store:
testUtils.js
import React from 'react';
import { createStore } from 'redux';
import { render } from '#testing-library/react';
import { Provider } from 'react-redux';
import reducer from '../reducers';
// https://testing-library.com/docs/example-react-redux
export const renderWithRedux = (
ui,
{ initialState, store = createStore(reducer, initialState) } = {},
options,
) => ({
...render(<Provider store={store}>{ui}</Provider>, options),
store,
});
So, based upon what you've shared I think the wrapper you'd want would look something like this:
import React from 'react';
import MyContext from 'contexts/MyContext';
// export so you can test that it was called with specific arguments
export dispatchMock = jest.fn();
export ProviderWrapper = ({ children }) => (
// place your mock dispatch function in the provider
<MyContext.Provider value={{ dispatch: dispatchMock }}>
{children}
</MyContext.Provider>
);
and in your test:
import React from 'react';
import RandomButton from '../RandomButton';
import { render, fireEvent } from '#testing-library/react';
import { ProviderWrapper, dispatchMock } from './testUtils';
describe('Button Render', () => {
it('click button', () => {
const { getByTestId, queryByText } = render(
<RandomButton />,
{ wrapper: ProviderWrapper }, // Specify your wrapper here
);
expect(getByTestId('jared-test-button')).toBeInTheDocument();
fireEvent.click(getByTestId('jared-test-button'));
// expect(queryByText('My name is frog')).toBeInTheDocument(); // won't work since this text is part of the parent component
// If you wanted to test that the dispatch was called correctly
expect(dispatchMock).toHaveBeenCalledWith({ type: 'SWITCH' });
})
})
Like I said, I've not had to specifically test redux hooks but I believe this should get you to a good place.
I have a simple component with a button, that when pressed fetches a comments JSON from placeholder API.
My enzyme test with mount() is failing, even though I can see that the state is updating in the CommentList component.
My manual tests in the browser display the comments fine.
My test with mount and a mock store passes.
I can even see that 2 li elements are created if I debug or console.log in CommentList.
Does it seem like the view is not being updated in mount after the redux state change?
Apologies for the amount of code below, I'm not sure which part is the culprit. The project can be cloned from https://github.com/Hyllesen/react-tdd
integration.test.js (failing test)
import React from "react";
import { mount } from "enzyme";
import Root from "Root";
import CommentList from "components/CommentList";
import moxios from "moxios";
beforeEach(() => {
moxios.install();
moxios.stubRequest("http://jsonplaceholder.typicode.com/comments", {
status: 200,
response: [{ name: "Fetched #1" }, { name: "Fetched #2" }]
});
});
it("can fetch a list of comments and display them", () => {
//Render entire app
const wrapped = mount(
<Root>
<CommentList />
</Root>
);
//Find fetchComments button and click it
wrapped.find(".fetch-comments").simulate("click");
wrapped.update();
//Expect to find a list of comments
expect(wrapped.find("li").length).toBe(2);
});
Root.js
import React from "react";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import reducers from "reducers";
import reduxPromise from "redux-promise";
export default ({
children,
store = createStore(reducers, {}, applyMiddleware(reduxPromise))
}) => {
return <Provider store={store}>{children}</Provider>;
};
CommentList.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchComments } from "actions";
class CommentList extends Component {
renderComments() {
console.log(this.props.comments);
return this.props.comments.map(comment => <li key={comment}>{comment}</li>);
}
render() {
const comments = this.renderComments();
//This is actually creating 2 li elements in the test
console.log("render", comments);
return (
<div>
<button className="fetch-comments" onClick={this.props.fetchComments}>
Fetch comments
</button>
<ul>{comments}</ul>
</div>
);
}
}
function mapStateToProps(state) {
return { comments: state.comments };
}
export default connect(
mapStateToProps,
{ fetchComments }
)(CommentList);
actions/index.js
import { FETCH_COMMENTS } from "actions/types";
import axios from "axios";
export function fetchComments() {
const response = axios.get("http://jsonplaceholder.typicode.com/comments");
return {
type: FETCH_COMMENTS,
payload: response
};
}
reducers/comments.js
import { FETCH_COMMENTS } from "actions/types";
export default function(state = [], action) {
switch (action.type) {
case FETCH_COMMENTS:
const comments = action.payload.data.map(comment => comment.name);
return [...state, ...comments];
default:
return state;
}
}
reducers/index.js
import { combineReducers } from "redux";
import commentsReducer from "reducers/comments";
export default combineReducers({
comments: commentsReducer
});
CommentList.test.js (test passing, using mock store)
import React from "react";
import { mount } from "enzyme";
import Root from "Root";
import CommentList from "components/CommentList";
import createMockStore from "utils/createMockStore";
let wrapped, store;
beforeEach(() => {
const initialState = {
comments: ["Comment 1", "Comment 2"]
};
store = createMockStore(initialState);
wrapped = mount(
<Root store={store}>
<CommentList />
</Root>
);
});
afterEach(() => {
wrapped.unmount();
});
it("Creates one li per comment", () => {
expect(wrapped.find("li").length).toBe(2);
});
it("shows text for each comment", () => {
expect(wrapped.render().text()).toEqual("Fetch commentsComment 1Comment 2");
});
It looks like your problem is caused by your moxios request stubbing. I think you need to wait for the response to be returned before calling update() on your wrapper.
beforeEach(() => {
moxios.install()
})
it('can fetch a list of comments and display them', done => {
// Render entire app
const wrapped = mount(
<Root>
<CommentList />
</Root>
)
// Find fetchComments button and click it
wrapped.find('.fetch-comments').simulate('click')
moxios.wait(() => {
let request = moxios.requests.mostRecent()
request
.respondWith({
status: 200,
response: [{ name: 'Fetched #1' }, { name: 'Fetched #2' }]
})
.then(function () {
wrapped.update()
expect(wrapped.find('li').length).toBe(2)
done()
})
})
})
I am trying to create a unit test using react,ava, etc..I am having issue creating a simple unit test to check if a method was called. My test should pass if the method has been called. However when i check the code coverage I get a message saying "function not covered". Below is the code I am using to test it.
import test from 'ava';
import React from 'react';
import { Cart } from 'components/Cart/CartDisplay';
import { shallow } from 'enzyme';
let props;
test.beforeEach(() => {
props = {
popError: () => {},
message: '',
count: 2,
displayCart:() => {},
onClose:() => {}
};
});
test('renders okay?', (t) => {
shallow(
<Cart {...props} />
);
t.pass('yes');
});
test('Cart Displayed okay?', (t) => {
props.displayCart();
t.pass('yes');
});
What am I doing wrong?
After a couple of tries, I was able to figure it out:
import test from 'ava';
import React from 'react';
import { Cart } from 'components/Cart/CartDisplay';
import { shallow,mount } from 'enzyme';
import sinon from 'sinon';
import {expect} from 'chai';
let props;
test.beforeEach(() => {
props = {
popError: () => {},
message: '',
count: 2,
displayCart:() => {},
onClose:() => {}
};
});
test('Cart Display called?', t => {
sinon.spy(Cart.prototype, 'cartDisplay');
const wrapper = mount(<BannerMessage />);
expect(Cart.prototype.componentDidMount.calledOnce).to.equal(true);
})