Jest mock a specific component from a module - reactjs

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()
})

Related

How to test with Jest whether action created by action creator (and not passed as prop) fires?

I try to test whether a given action is really fired in a component. It could be easy if the callback with action was delivered as a prop, but it's not clear for me how to do it in my case. Here is the component:
export const Modal = (props: appendProps): JSX.Element => {
const { items, activeScope } = props;
const primary = items[0] === activeScope;
const { closeInput, appendItem } = useDispatchAction();
{...}
<Button
variant="contained"
size="large"
className="modal-content__button"
color="secondary"
onClick={() => {
closeInput();
}}
>
Zamknij
</Button>
useDispatchAction is below:
import { useDispatch } from 'react-redux';
import { bindActionCreators } from 'redux';
import { actionCreators } from '../redux';
const useDispatchAction = () => {
const dispatch = useDispatch();
return bindActionCreators(actionCreators, dispatch);
};
export default useDispatchAction;
And test itself below
describe('Test button actions', () => {
const closeInput = jest.fn();
jest.mock('../../src/hooks/useDispatchAction', () => ({closeInput}));
beforeEach(() => {
render(<Modal />);
});
afterEach(() => cleanup());
test('component fires closeInput', () => {
const closeButton = screen.getByRole('button', { name: 'Zamknij' });
expect(closeButton).toBeInTheDocument();
userEvent.click(closeButton);
expect(closeInput).toBeCalled();
});
});
but this keep telling me that function has been called 0 times and not 1 at least
When you jest.mock, you get a function that returns undefined by default. So, if useDispatchAction returns undefined, you can't go const { closeInput, appendItem } = useDispatchAction();, because you cannot destructure from undefined.
However, you can supply a second argument to jest.mock:
const closeInput = jest.fn()
jest.mock('../../src/hooks/useDispatchAction', () => ({ closeInput }))
Now you've mocked useDispatchAction to return something that looks more like your original function, except that the action returns something you can assert was called.
You can test it like this:
expect(closeInput).toHaveBeenCalled()
I think this should work, but I haven't tested this particular code.
What finally works is:
const actions = {
closeInput: jest.fn(),
appendItem: jest.fn(),
};
jest.mock('../../src/hooks/useDispatchAction', () => () => actions);
describe('Given Modal component', () => {
describe('when "Zamknij" button is clicked', () => {
it('should call closeInput function', async () => {
actions.closeInput.mockClear();
const { findByText } = render(<Modal />);
const closeButton = await findByText('Zamknij');
fireEvent.click(closeButton);
expect(actions.closeInput).toHaveBeenCalled();
});
});
});

React Js - Enzyme test if setState is triggered

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

Struggling to mock custom react hook

I'm wondering if anyone can help me with where I'm going wrong here...
Trying to mock a custom hook but jest/enzyme is not recording any calls to the function.
My test:
const mockHandleEditProtection = jest.fn();
const mockUseEditProtection = jest.fn(() => [null, mockHandleEditProtection]);
jest.mock('../../common/useEditProtection', () => ({
__esModule: true,
default: () => mockUseEditProtection,
}));
describe('my test', () => {
const wrapper = mount(<AComponent proposal={mockProposalDataWithEnrichedValues}/>)
it('should call useEditProtection with proposal object', () => {
expect(mockUseEditProtection).toBeCalledWith(mockProposalDataWithEnrichedValues);
});
it('should call edit protection when edit button is clicked', () => {
wrapper.find(TableCell).at(4).find(Button).at(0).simulate('click');
expect(mockHandleEditProtection).toBeCalled();
});
})
A basic example of how the hook is used in a component...
const AComponent = ({proposal}) => {
const [EditWarning, editProtection] = useEditProtection(proposal);
return <div>
{EditWarning}
<button onClick={editProtection}>Edit</button>
</div>
};
I am very confused as to why it isn't working so any pointers are appreciated!

How to use jest.spyOn with functional react component

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} />);

window.history.back() not being called - jest enzyme

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();
});
});

Resources