I am new to jest. My component look like this.
class ComponentToTest extends React.Component {
searchName = () => {
return axios.get(...)
}
render() {
return(
<Form onSubmit={handleSubmit(save)}>
<SomeChildComponent searchName={ (searchTerm: string) => this.searchName(searchTerm)} />
</Form>
)
}
}
`ComponentToTest.test.js`
it('should call `handleSubmit` with `save`', () => {
const store = createStore((state) => state, { app: {} });
const onSave = jest.fn().mockReturnValue(() => {});
const props: any = { handleSubmit: onSave };
const tree = mount(
<Provider store={store}>
<ComponentToTest {...props} />
</Provider>,
);
tree.find('button').simulate('submit');
expect(onSave).toHaveBeenCalled();
});
I am getting error after running this test. is there any way to mock the call to searchName and return promise?
Related
I am using this "async image" component but I have problems in unit tests, for the "load" success section with the image
import { useEffect, useState } from 'react'
const AsyncImage = (props) => {
const [loadedSrc, setLoadedSrc] = useState(null)
useEffect(() => {
setLoadedSrc(null)
if (props.src) {
const handleLoad = () => {
setLoadedSrc(props.src)
}
const image = new Image()
image.addEventListener('load', handleLoad)
image.src = props.src
return () => {
image.removeEventListener('load', handleLoad)
}
}
}, [props.src])
if (loadedSrc === props.src) {
return (
<img {...props} />
)
}
return null
}
export default AsyncImage
Unit test:
global.Image = class {
constructor() {
this.onload = jest.fn();
this.addEventListener = jest.fn()
this.removeEventListener = jest.fn()
setTimeout(() => {
this.onload();
}, 50);
}
};
test("render AsyncImage", async () => {
const logo = LOAD_SUCCESS_SRC
let container
await act(async () => {
container = render(
<Router>
<Provider store={store}>
<AsyncImage
src={logo}
alt="test.png"
className='img-fluid img-catalogCards'
/>
</Provider>
</Router>
)
})
})
At the beginning of the unit test add the image class, it enters the setTimeout but still does not enter the handleLoad function
How do I get to the handleLoad function when loading the image so that it enters the return section of the image?
To render the container component connected to redux store, I'm using this function to render the component
function render(Component, props, storeData) {
const mockStore = configureStore([thunkMiddleware]);
const store = mockStore(storeData);
return mount(
<Provider store={store}>
<Component {...props} />
</Provider>
);
}
Now, I need to test the props change of the Component rendered but it looks like ReactWrapper.setProps only works for the Root component.
MyComponent is a container component that is connected to store using connect.
describe('MyComponent', () => {
it('should work at props change', () => {
const wrapper = render(MyComponent, { value: 1 }, initialStoreValue);
wrapper.find(MyComponent).setProps({ value: 2});
// then expect something.
});
});
Few things to consider:
redux-mock-store allows to pass a function as state source - redux-mock-store will call that function every time
to trigger re-rendering component with useSelector or connect() we can just dispatch() with any action type. Literally any.
We need to wrap updating store into act() or otherwise it might not work and definitely will complain with console.error.
So keeping that in mind we can enhance render:
function render(Component, props, initialStoreData) {
let currentStoreData = initialStoreData;
const mockStore = configureStore([thunkMiddleware]);
const store = mockStore(() => currentStoreData);
const wrapper = mount(
<Provider store={store}>
<Component {...props} />
</Provider>
);
const updateStore = (newStoreData) => {
currentStoreData = newStoreData;
act(() => {
store.dispatch({ type: '' }); // just to trigger re-rendering components
});
}
return { wrapper, updateStore };
}
And then in test:
describe('MyComponent', () => {
it('does something when store changes somehow', () => {
const { wrapper, updateStore } = render(MyComponent, { value: 1 }, initialStoreValue);
updateStore({ someReduxValue: 2});
// then expect something.
});
});
I've been following this tutorial to connect my React app to Firebase. However, I'd like to refactor the "withAuthentication" component to be functional, like the rest of my app.
The component I get as a result of following the tutorial is:
const withAuthentication = Component => {
class WithAuthentication extends React.Component {
constructor(props) {
super(props);
this.state = {
authUser: null,
};
}
componentDidMount() {
this.listener = this.props.firebase.onAuthUserListener(
authUser => { // Valid user
this.setState({ authUser });
},
() => { // Invalid user
this.setState({ authUser: null });
},
() => { // No user
this.setState({ authUser: null });
}
);
}
componentWillUnmount() {
this.listener();
}
render() {
return (
<AuthUserContext.Provider value={this.state.authUser}>
<Component {...this.props} />
</AuthUserContext.Provider>
);
}
}
return withFirebase(WithAuthentication);
};
export default withAuthentication;
To convert it to be functional, so far I have:
const withAuthentication = Component => {
const WithAuthentication = ({firebase, props}) => {
const [authUser, setAuthUser] = useState(null);
useEffect(() => {
const listener = firebase.onAuthUserListener(
authUser => { // Valid user
setAuthUser(authUser);
},
() => { // Invalid user
setAuthUser(null);
},
() => { // No user
setAuthUser(null);
}
);
}, [firebase])
return (
<AuthUserContext.Provider value={authUser}>
<Component {...props} />
</AuthUserContext.Provider>
);
}
return withFirebase(WithAuthentication);
};
export default withAuthentication;
Firstly, I'm not sure whether this is the correct approach - happy to be told if not! Secondly, the original component has the componentWillUnmount() function which removes the listener - I'm not sure how to do this in my functional version. Can anyone advise?
You need to add a return inside the useEffect as in this post
useEffect(() => {
const listener = firebase.onAuthUserListener(
authUser => { // Valid user
setAuthUser(authUser);
},
() => { // Invalid user
setAuthUser(null);
},
() => { // No user
setAuthUser(null);
}
);
return (() => listener())
}, [firebase])
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 want to write unit test for React component's method.
The component's code is
export class LoginForm extends Component {
constructor() {
super();
this.tryLoginProp = this.tryLoginProp.bind(this)
}
render() {
return (
<div className="login-form">
<div className="form-input">
<CustomButton label="Log in"
class="login-button"
action={this.tryLoginProp}
id="login-button-loginform"
renderSpinner={this.props.renderSpinner}
/>
</div>
</div>
)
}
tryLoginProp () {
if (!this.props.renderSpinner) {
this.props.tryLoginProp()
}
}
}
const mapStateToProps = (state) => {
return {
login: state.login,
renderSpinner: state.layout.renderSpinner
};
};
const mapDispatchToProps = (dispatch) => ({
tryLoginProp: () => {
dispatch(tryLogin())
}
});
export default connect(mapStateToProps, mapDispatchToProps)(LoginForm);
I want to write unit test for tryLoginProp method, but I am not getting how to mock this.props.tryLoginProp function and pass the test case.
Current unit test is as follows:
describe('<LoginForm />', () => {
const initialState = {renderSpinner: false};
let wrapper;
beforeEach(() => {
wrapper = mount(<LoginForm {...initialState}/>);
});
it('renders without expolding', () => {
expect(wrapper.length).toEqual(1);
});
it('tryLoginProp should dispatch an action', () => {
expect(wrapper.tryLoginProp()). //test tryLoginProp method.
})
});
Please help me to write proper test case for this method.
Thanks
You can use wrapper.instance().tryLoginProp() to call the method...like this I think without testing it
it('tryLoginProp should dispatch an action', () => {
const mockedTryLoginProp = jest.fn();
const wrapper = shallow(
<LoginForm
tryLoginProp={mockedTryLoginProp}
renderSpinner={false}
/>
);
wrapper.instance().tryLoginProp();
expect(mockedTryLoginProp).toHaveBeenCalled();
})
On a side note, you may consider naming the internal function differently than the one being passed in to avoid confusion