I am using enzyme to test my react application. This is my react component:
import React from 'react';
const Child = ({name, setName}) => {
return (
<div>
<button onClick={() => setName('test')}>setName</button>
<h2>Name: {name}</h2>
</div>
);
};
export default Child;
By clicking the button i change the setName which is passed as bellow:
function App() {
const [name, setName] = useState();
return (
<div className="App">
<Child setName={setName} name={name}/>
</div>
);
}
export default App;
So, testing Child component i created the next test to test when user clicks the button should trigger setName.
describe('Should test Child component', () => {
const wrapper = (props) => shallow(<Child {...props}/>)
test('should trigger setName', () => {
const mockFun = jest.fn()
const r = wrapper({setName: jest.fn() });
const btn = r.find('button');
btn.simulate('click');
expect(r.props().setName).toHaveBeenCalled()
})
})
Running the test i get:
Error: expect(received).toHaveBeenCalled()
Matcher error: received value must be a mock or spy function
Received has value: undefined
Why i get this error and how to make my test to be valid?
I can't test it right now but I think modifying it like this should do it
describe('Should test Child component', () => {
const wrapper = (props) => shallow(<Child {...props}/>)
test('should trigger setName', () => {
const mockFun = jest.fn()
const r = wrapper({setName: mockFun });
const btn = r.find('button');
btn.simulate('click');
expect(mockFun).toHaveBeenCalled()
})
})
Also see this one as a reference same issue How to test onClick props of a button using Jest and Enzyme
Related
What's the best way to do this so I'm not recreating the button structure all over again in my mock ?
Mocking a exported function component from a module of multiple components
Example:
jest.mock('#moduleComponents', () => ({
...jest.requireActual('#moduleComponents'),
Button: () => jest.fn(),
}))
Problem here is Button is an named exported component so () => jest.fn() breaks.
I can do:
jest.mock('#moduleComponents', () => ({
...jest.requireActual('#moduleComponents'),
Button: () => <div>fsdf</div,
}))
That works but I don't want to recreate the structure of the Button using JSX
Context on what I'm trying to test:
Component
import { Button } from '#moduleComponents'
const MyComponent = () => {
const onClickHandler = () => {
....code in here
}
return(
<div>
<Button onClick={onClickHandler}>My Button</Button>
</div>
)
}
test component:
const mockedButton = jest.fn()
jest.mock('#moduleComponents', () => ({
...jest.requireActual('#moduleComponents'),
Button: () => mockedButton,
}))
test('button was fired once', () => {
render(<MyComponent />)
button = screen.getByText('My Button')
fireEvent.click(button)
expect(mockedButton).toBeCalled()
})
As you can see, I have a function called trueFn that I export into App. I then call it every time I click the button. I'm just unsure how to get jest to mock that function in the test file. How can I make sure the mocked function is called when I click the button?
/*
./trueFn.js
*/
export const trueFn = () => {
return true;
}
/*
App.js
*/
import React from 'react';
import { trueFn } from './trueFn';
const App = () => {
return (
<Button onClick={trueFn}>Click me</Button>
)
}
/*
App.test.js
*/
const getComponent = () => {
return render (
<App />
)
}
describe('App.js Component', () => {
it('should run trueFn on button click', () => {
const mockTrueFn = jest.fn();
const component = getComponent();
expect(mockTrueFn).toHaveBeenCalledTimes(1);
})
})
You need to use a spy
Something like this should work:
const trueFn = require('./trueFn.js');
describe('App.js Component', () => {
it('should run trueFn on button click', () => {
const spy = jest.spyOn(trueFn);
getComponent();
// do an action to call your button, something like:
const button = screen.getByTestId('element');
userEvent.click(button);
expect(spy).toHaveBeenCalled();
})
})
I have a component which has multiple useState calls. It renders button when one of the state is populated. How can I mock populated state to be able to test if the button is rendered?
const Component = () => {
const [csvData, setCsvData] = useState([]);
const [loading, setLoading] = useState(false);
const [errors, setErrors] = useState([]);
const handleCSVLoad = (data) => {
// ...some logic to process the data
setCsvData(data)
}
// ...other functions
return(
<>
// ...some other components
<CSVReader
onFileLoaded={handleCSVLoad}
/>
csvData[0]&& <button data-testId='submit_button'>Submit</button>
</>
)
the csvData is populated when CSV data is loaded with react-csv-reader.
I want to be able to write test like that:
it('does render Submit button', () => {
const { queryByTestId } = render(<Component />);
const submitButton = queryByTestId('submit_button');
expect(submitButton).toBeTruthy();
});
I tried to mock useState like this:
import React, { useState as useStateMock } from 'react';
jest.mock('react', () => ({
...jest.requireActual('react'),
useState: jest.fn(),
}));
describe('testing useState mocked', () => {
const setState = jest.fn();
const useStateMock = () => [[{email: 'test#test.com'}], setState];
jest.spyOn(React, 'useState').mockImplementation(useStateMock);
afterEach(() => {
jest.clearAllMocks();
});
});
but it breaks as errors state is now defined and submit button is not rendered when error is not an empty array.
I want to test if the handleAddBookToCart function is called when I clicked on the button using Jest.spyOn.
Thank you in advance for your help.
const HomeComponent = props => {
const { addBookToCart } = props;
const handleAddBookToCart = id => {
addBookToCart(id);
};
return (
<button onClick={() => handleAddBookToCart(id)}></button>
)
}
//Container
const mapDispatchToProps = dispatch => ({
addBookToCart: idBook => dispatch(cartOperations.addBookToCart(idBook))
});
//file test
describe('Test home component', () => {
let container;
let wrapper;
let component;
beforeEach(() => {
wrapper = mount(
<Provider store={store}>
<HomeContainer />
</Provider>
);
container = wrapper.find(HomeContainer);
component = container.find(HomeComponent);
it('calls the handleAddBookToCart function when the button is clicked', () => {
const spy = jest.spyOn(???)
component.find('button').simulate('click');
expect(spy).toHaveBeenCalled();
});
Should you must use spyOn? You can just pass a jest mock fn as the addBookToCart prop
it('calls the handleAddBookToCart function when the button is clicked', () => {
const props = {
addBookToCart: jest.fn(),
...your other props
}
const component = shallow(<HomeComponent {...props} />)
component.find('button').simulate('click');
expect(props.addBookToCart).toHaveBeenCalled();
});
An easy way would be to send a mock into the property addBookToCart and spy on that when the button is clicked.
So when you create/mount/shallow your component for the unit test, try the following (mount coming from enzyme but feel free to use your existing method of testing components):
const spy = jest.fn();
const component = mount(<HomeComponent addBookToCart={spy} />);
I wrote a simple unit test for the following. I am new to React JS testing - Trying to run a test using jest and enzyme.
render() {
return (
<div>
<div className="not-found">
<div className='_2'>WAS NOT FOUND</div>
<div onClick={() => {window.history.back()}} className='not-found-
btn' href='/'>GO BACK</div>
)
}
}
The file looks simple, there are no props and the only thing not being covered when the test is running is onClick . How could I test onClick and make sure the test is 100 % covered. Thanks
<div onClick={() => {window.history.back()}} className='not-found-
btn' href='/'>GO BACK</div>
file.test.js
// jest mock functions (mocks this.props.func)
const onClick = jest.fn();
// defining this.props
const baseProps = {
onClick,
}
describe(' Test', () => {
let wrapper;
let tree;
beforeEach(() => wrapper = shallow(<Component{...baseProps } />));
// before each test, shallow mount the Component
it('should render correctly', () => {
tree = renderer.create(<NotFound {...baseProps} />)
let treeJson = tree.toJSON()
expect(treeJson).toMatchSnapshot();
tree.unmount()
});
it('calls onClick event ', () => {
const mockOnClick = jest.fn();
const wrapper = shallow(
<NotFound onClick={mockOnClick} className='not-found-btn' />
);
const component = wrapper.shallow();
component.find('GO BACK').simulate('click');
expect(mockOnClick.mock.calls.length).toEqual(1);
I'd avoid using window history and instead use react-router-dom for MPAs. In addition, instead of using an anonymous function, you can use a PureComponent class (it's similar to a Component class, but it doesn't update state) with a method class function.
Working example: https://codesandbox.io/s/j3qo6ppxqy (this example uses react-router-dom and has a mix of integration and unit testing -- see the tests tab at the bottom of the page to run the tests and look for __test__ folders to see the code)
components/NotFound/notfound.js
import React, { PureComponent } from "react";
import { Button } from "antd";
export default class NotFound extends PureComponent {
handlePageBack = () => this.props.history.push("/");
render = () => (
<div className="notfound">
<h1>404 - Not Found!</h1>
<Button type="default" onClick={this.handlePageBack}>
Go Back
</Button>
</div>
);
}
components/NotFound/__tests__/notfound.test.js (as mentioned here, you can also test the class method, if desired)
import React from "react";
import { shallowComponent } from "../../../tests/utils";
import NotFound from "../notfound";
const mockGoBack = jest.fn();
const initialProps = {
history: {
goBack: mockGoBack
}
};
/*
the shallowComponent function below is a custom function in "tests/utils/index.js" that
simplifies shallow mounting a component with props and state
*/
const wrapper = shallowComponent(<NotFound {...initialProps} />);
describe("Not Found", () => {
it("renders without errors", () => {
const notfoundComponent = wrapper.find("div.notfound");
expect(notfoundComponent).toHaveLength(1);
});
it("pushes back a page when Go Back button is clicked", () => {
wrapper.find("Button").simulate("click");
expect(mockGoBack).toHaveBeenCalled();
});
});
window.history.back is being called, but it has a delay time. I can make it work using a Promise:
const Component = ()=> (<div>
<button onClick={()=> window.history.back()} className="btn btn-back">
Back
</button>
</div>)
Component.test.js
import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
const delayAction = (fn, time = 1000) =>
new Promise((resolve) => {
fn();
setTimeout(() => {
resolve();
}, time);
});
let container = null;
describe("App tests", () => {
afterEach(() => {
//unmount Component...
});
beforeEach(() => {
//mount Component
});
it("should call history.back()", async (done) => {
const btnBack = container.querySelector(".btn-back");
await act(() =>
delayAction(() => btnBack.dispatchEvent(new MouseEvent("click", { bubbles: true })))
);
// asserts..
done();
});
});