How to unit test a Redux action in a component inside a connected Redux component with Jest - unit-testing

I'm using jest and enzyme to unit test my React application and I'm struggling with testing connected components.
I do have a simple component which the following logic:
class LoginPage extends React.Component {
componentDidMount() {
if (!this.props.reduxReducer.appBootstrapped) {
this.props.dispatch(ReduxActions.fadeOutAndRemoveSplashScreen(500));
}
}
render() {
return (
<div data-page="login-page" >
<div>This is the login page.</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
reduxReducer: state.reduxReducer
}
};
export default connect(mapStateToProps, null)(LoginPage);
So, this is a component which displays a <div /> element containing some text, but the important part that I want to test is that when the component is mounted, an action is dispatched to hide the splash screen.
I want this only to happen when the application is not bootstrapped.
I do have a simple unit test to test that the component is rendered:
describe("[LoginPage Component]", () => {
it("Renders without a problem.", () => {
// Act.
const wrapper = mount(
<LoginPage store={ reduxStore } />
);
// Assert.
expect(wrapper.find("div[data-page=\"login-page\"]").length).toBe(1);
});
});
The reduxStore property is my actual redux store, created with the following code:
const reduxStore = createStore(
combineReducers(
{
reduxReducer
}
)
);
Now, how can I test the componentDidMount() method, and more in special, test that the redux action fadeOutAndRemoveSplashScreen() is only called when the application is not bootstrapped yet.
I do think that I need to mock my redux store, however, I'm a newbie on this and don't now how to get started, so an example will be highly appreciated.
If any other thoughts on my implementation, feel free to provide some advice.
Kind regards

I wouldn't use the raw dispatch method to send off an action. I would use mapDispatchToProps. This makes your action directly available in your component props - here we use ES6 destructing as a short hand in the connect method.
Then instead of mocking the redux store I would just test your component without it. Try adding an export to your class (first line). For example:
export class LoginPage extends React.Component {
componentDidMount() {
if (!this.props.reduxReducer.appBootstrapped) {
// make sure that you are using this.props.action() not
// just the action(), which is not connected to redux
this.props.fadeOutAndRemoveSplashScreen(500);
}
}
render() {
return (
<div data-page="login-page" >
<div>This is the login page.</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
reduxReducer: state.reduxReducer
}
};
export default connect(mapStateToProps, {
fadeOutAndRemoveSplashScreen: ReduxActions.fadeOutAndRemoveSplashScreen
})(LoginPage);
Then in your test instead of importing the connected component, import the class:
import ConnectedLoginPage, { LoginPage } from '/path/to/component';
Then simply pass the LoginPage whatever props you want to test with. So we will set your appBooststrapped to false, and then pass the action as a sinon spy:
const spy = sinon.spy();
const reduxReducer = {
appBootstrapped: false, // or true
}
const wrapper = mount(
<LoginPage reduxReducer={reduxReducer} fadeOutAndRemoveSplashScreen={spy} />
);
// test that the spy was called
expect(spy.callCount).to.equal(1);
This makes the test much simpler, and more importantly you are testing the component behavior - not Redux.

Related

Mock named exports for testing using Jest

I have a Helper.js file with several helper functions as below that is being used in different components.
export function buildOptions(elem) {
var oList=[];
for (var i=0; i < field.length; i++) {
oList.push (
<option value={options[i]["id"]}>
{options[i][elem]}
</option>
)
}
return oList;
}
export function B(){
.....
}
Here is a component which makes use of the function defined in Helper.js file. I am writing tests for the component and I would like to mock the external function being called here.
import React from 'react';
import ReactDOM from 'react-dom';
import { buildOptions, A} from './Helper.js';
class DemoComponent extends React.Component {
constructor(props) {
super(props);
}
add(e, index) {
....
}
render() {
var o_list=buildOptions("name");
return (
<div>
...
<select required className={selectClass} >
{o_list}
</select>
...
<button type="button" onClick={(e) => this.add(e, this.props.index)}>
Add
</button>
</div>
);
};
}
I am new to Jest/Enzyme and I am unable to figure out how to mock the external function buildOptions. I am unable to figure out how to mock the external buildOptions function.Could anyone please help me with this.
Here is my test code:
import React from 'react';
import { mount, shallow } from 'enzyme';
import { buildOptions } from '../components/Helper.js';
import DemoComponent from '../components/DemoComponent';
describe('Democomponent', () => {
it('should render required elements', () => {
const wrapper = shallow(
<DemoComponent
index={0}/>
);
//
tests
});
Since you want to mock a named exported function, there is a special trick for that which involves importing all named exports with an * before your tests.
// your test file
import * as Helper from './Helper.js';
const originalBuildOptions = Helper.buildOptions;
Helper.buildOptions = jest.fn();
beforeEach(() => {
jest.clearAllMocks();
// Reset to original implementation before each test
Helper.buildOptions.mockImplementation(originalBuildOptions);
});
test('my test', () => {
// Mock for this test only (will be restored by next `beforeEach` call)
Helper.buildOptions.mockImplementation(() => 'your mock');
});
You can also mock default and named exports directly on import. The official jest documentation uses this method as of 2023. Updating to your use case:
// Your test file
import { buildOptions } from './Helper.js';
jest.mock('./Helper.js', () => {
const originalModule = jest.requireActual('./Helper.js');
// Mock any module exports here
return {
__esModule: true,
...originalModule,
// default: jest.fn(() => 'mocked default export example'),
// Named export mocks
buildOptions: jest.fn(),
};
});
This also works for installed packages as well. For example, I often override specific react-router hooks like useSubmit using the same style of import.
See the official documentation on partial jest named mocks here: https://jestjs.io/docs/mock-functions#mocking-partials

unit-testing component whose props gets value from react navigation component

How can I write a unit test for a React Native component which gets its state's value from React Navigation, more specific this.props.navigation.state.params? This is the component class:
class RecAreas extends Component{
static navigationOptions =({navigation}) => ({
title: navigation.state.params.title,
headerStyle: {
backgroundColor: "#000000",
},
headerTintColor: '#facf33',
});
constructor(props){
super(props);
const params = this.props.navigation.state.params;
this.state={
key:params.gymId,
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
}),
};
this.mainRef = db.ref("facilities");
}
componentDidMount(){
this.listenForItem(this.mainRef)
}
I Could not figure out how to pass the nested function as props for the unit test. In this case this.props.navigation, this.props.navigation.state, this.props.navigation.state.params are all objects. How do I mock them for unit test? In the below example I get TypeError: Cannot read property 'gymId' of undefined which makes sense because params is not defined. I need help to resolve this. This is a unit test for the component. Thank you in advance!
(PS: it would be great to know as well how to mock dataSource props as well. One way I thought I could do this is to make a fake dataSource data structure (print out the dataSource and look at its structure). Any pointers would be helpful. Thanks!)
import React from "react";
import RecAreas from "../../app/screens/RecAreas";
import renderer from "react-test-renderer";
describe ("<RecAreas", () => {
it("renders without crashing", () => {
const navigationMock = {state: jest.fn()};
const rendered = renderer.create(<RecAreas navigation=
{navigationMock}/>).toJSON();
expect(rendered).toBeTruthy();
expect(rendered).toMatchSnapshot();
});
}
hey you have mock the props also to generate the tree for snapshot
like this
import React from "react";
import RecAreas from "../../app/screens/RecAreas";
import renderer from "react-test-renderer";
describe ("<Test", () => {
it("renders without crashing", () => {
const navigationMock = { state: { params: { gymId: "1234" } } };
const rendered = renderer
.create(<RecAreas navigation={navigationMock} />)
.toJSON();
expect(rendered).toBeTruthy();
expect(rendered).toMatchSnapshot();
});
})
or you try Enzyme allows for direct manipulation of the props and state of the components

How to test react component correctly?

Recently I am learning to test React with jest and enzyme, It seems hard to understand what a unit test is it, my code
import React from "react";
class App extends React.Component {
constructor() {
super();
this.state = {
value: ""
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
const value = e.target.value;
this.setState({
value
});
}
render() {
return <Nest value={this.state.value} handleChange={this.handleChange} />;
}
}
export const Nest = props => {
return <input value={props.value} onChange={props.handleChange} />;
};
export default App;
and my test
import React from "react";
import App, { Nest } from "./nest";
import { shallow, mount } from "enzyme";
it("should be goood", () => {
const handleChange = jest.fn();
const wrapper = mount(<App />);
wrapper.find("input").simulate("change", { target: { value: "test" } });
expect(handleChange).toHaveBeenCalledTimes(1);
});
IMO, the mocked handleClick will intercept the handleClick on App,
if this is totally wrong, what's the right way to use mock fn and test the handleClick be called.
Another: I search a lot, read the similar situations, seem like this iscontra-Unit Test,
Probably I should test the two component separately, I can test both components,
test the
<Nest value={value} handleChange={handleChange} />
by pass the props manually, and then handleChangeinvoked by simulate change
it passed test.
but how can I test the connection between the two?
I read
some work is React Team's Work
...
I don't know which parts I have to test in this case, and Which parts react already tested and don't need me to test. That's confusing.
You should take the path of testing the Nest component in isolation first, passing your mocked handleChange as a prop, to verify that input changes are being propagated.
If you want to test the state part, then you can get the instance of your App class from enzyme and call that method directly:
it("should update the Nest value prop when change is received", () => {
const wrapper = mount(<App />);
const instance = wrapper.instance()
instance.handleChange( { target: { value: "test" } })
const nestComponent = wrapper.find("Nest").first()
expect(nestComponent).prop('value').toEqual('test');
});
This a very very basic, almost not needed to test piece of code, but it will get your test coverage up if that's what you're after.
Doc for instance: http://airbnb.io/enzyme/docs/api/ReactWrapper/instance.html
If you want to test for the connection. From what I see, the nest component is a child component inside the App component. You could test that <App /> contains `.
describe('<App />', () => {
it('should contain a nest component', () => {
const wrapper = mount(<App />);
expect(wrapper.find(<Nest />)).toHaveLength(1);
});
});
Secondly, since the onChange event on the nest component updates the state in the App component, you can also test for state changes since its a behavior you expect.
it('should update state', () => {
//find input and simulate change with say {value: 'new value'} and then
expect(wrapper.state().value).toBe('newValue');
});
I hope this helps.

React.js and Jasmine Spies

Using basic test-utils and jasmine for unit testing.
How do you spy on a function inside a react component?
test.js:
class Test extends React.Component {
handleClick() {
// Do something
}
render() {
return (
<div className="test-class" onClick={this.handleClick}>Test</div>
);
}
}
const React = require('react-with-addons');
const RequireJsTest = require('requirejs-test');
const Utils = React.addons.TestUtils;
const Test = require('./test');
describe('test', () => {
it('should test', function() {
const test = Utils.renderIntoDocument(<Test/>);
const el = Utils.findRenderedDOMComponentWithClass(test, 'test-class');
spyOn(test, 'handleClick');
Utils.Simulate.click(el);
expect(test.handleClick).toHaveBeenCalled();
});
});
I'm getting the following error:
Expected spy handleClick to have been called. (1)
Any ideas? Thanks!
To be honest, I haven't tested react apps yet, but try the method (the last test in describe block), which I've just found in enzyme readme doc.
I think you should spy on component class prototype method before rendering component:
spyOn(Test.prototype, 'handleClick');
// and then
expect(Test.prototype.handleClick).toHaveBeenCalled();

React how to test out react compoennt with setTimeout

I am run into this test problem, which i am not sure how to test component with settimeout. anyone have suggestion on how to test out the following component with settimeout? Many thanks
import React, { PropTypes, Component } from 'react';
import styles from '../../../css/notification.css';
export default class Notification extends Component {
static propTypes = {
tagMetaUpdate: PropTypes.shape({
submitUpdatedTagDataSuccessful: PropTypes.bool
}),
actions: PropTypes.shape({
resetUpdatedTagDataSuccessfulFlag: PropTypes.func
})
};
constructor(props) {
super(props);
this.state = {
showMessage: true
};
}
hideMessage(actions) {
this.timer = setTimeout(() => {
this.state.showMessage = false;
actions.resetUpdatedTagDataSuccessfulFlag();
this.forceUpdate();
}, 3000);
}
render() {
const { tagMetaUpdate, actions } = this.props;
const output = (tagMetaUpdate.submitUpdatedTagDataSuccessful && this.state.showMessage) ?
<div className={styles.notification}>Tag meta data successfully edited.</div> : null;
if (this.props.tagMetaUpdate.submitUpdatedTagDataSuccessful) {
this.hideMessage(actions); // HERE IS THE BIT BLOCKING ME
}else {
this.state.showMessage = true;
}
return <div>{output}</div>;
}
}
use sinnon Faking time will solve the problem
http://sinonjs.org/
scroll to Faking time
It is generally ill-advised to mutate the state directly. Try to use this.setState({}), and when you call setState, this.forceUpdate is unnecessary. React would update your component by itself.
Also, try to use the render method to only perform rendering. No mutating state and all that.
If you directly do this.setState({ showMessage: true }) in the render method, react would complain about it - that it's not allowed.
React provides a lifecycle hook methods called componentWillRecieveProps and componentWillUpdate. You can use them to check if the prop has changed and then do setState accordingly.
componentWillReceiveProps(nextProps){
if(!_.isEqual(nextProps, this.props) {
if(nextProps.tagMetaUpdate.submitUpdatedTagDataSuccessful){
this.setState({ showMessage: true })
this.timer = setTimeout(() => {
this.setState({ showMessage: false })
actions.resetUpdatedTagDataSuccessfulFlag()
}, 3000)
}
}
}
Note that:
by using setState in the setTimeout, I've eliminated the need to use forceUpdate()
i use lodash to do a check to see if the prop has changed from the previous render to the current one. React would re-render the component for one of these reasons:
if the prop changes
if the component's state updates
if one of its parents need to update for either of the two reasons.
So we do a check to see that the update is only happening because the prop has changed, and if it has, then do a setState to show the message and a set timeout to not show the message after 3 seconds.
These are just usage advices. Can you tell me more about the framework you use for unit testing. I might be able to help a bit if it's jest.