How do I unit test the doStuff method below without having to create a full blown React.ChangeEvent object?
// my-form.tsx
export const doStuff = (changeHandler: ChangeHandler, e: React.ChangeEvent<HTMLInputElement>) => {
// A call to another function takes place here, but for brevity, I'm using console.log
console.log(e);
changeHandler(e);
}
interface ChangeHandler {
(e: React.ChangeEvent<HTMLInputElement>): void;
}
interface MyFormProps {
changeHandler: ChangeHandler,
username: string;
password: string;
}
const MyForm: React.SFC<MyFormProps> = ({changeHandler, username, password}) => (
<form>
<div>
<label htmlFor="username">Username</label>
<input
type="text"
name="username"
value={username}
onChange={doStuff.bind(null, changeHandler)} />
</div>
<div>
<label htmlFor="password">Password</label>
<input
type="text"
name="password"
value={password}
onChange={doStuff.bind(null, changeHandler)} />
</div>
<button>Submit</button>
</form>
);
export default MyForm;
Below is my test file. The first test is what I'd like to do in order to test doStuff. The 2nd test, I believe, is how I can also test doStuff.
// my-form.test.tsx
import MyForm, { doStuff } from './my-form.tsx';
describe('MyForm' => {
it('should call the changeHandler', () => {
// Arrange
const changeHandlerMock = jest.fn();
// Act
// How can I pass a mock for the 2nd parameter?
// doStuff(changeHandlerMock, {});
// Assert
expect(changeHandlerMock.called.length).toEqual(1);
});
it('should call the changeHandler too', () => {
// Arrange
const changeHandlerMock = jest.fn();
// Act
const wrapper = shallow(
<MyForm
changeHandler={changeHandlerMock}
username={username}
password={password} />
);
wrapper.find(`input[name='username']`).simulate('change');
// Assert
expect(changeHandlerMock.called.length).toEqual(1);
})
});
Alternatively, I can get rid of doStuff and change the markup, for each input element, to:
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e);
changeHandler(e);
}} />
The downside is that if I need to make changes to the eventHandler, I'll have to do it in every place it's declared, hence the doStuff method.
For completeness, here is my container component:
interface MyFormContainerState {
username: string;
password: string;
}
// my-form-container.tsx
class MyFormContainer extends React.Component<{}, MyFormContainerState> {
constructor(props: {}) {
super(props);
this.state = {
username: '',
password: ''
};
}
public render() {
const { username, password } = this.state;
return (
<MyForm
changeHandler={this.changeHandler}
username={username}
password={password} />
);
}
public changeHandler = (e: React.ChangeEvent<HTMLInputElement>): void => {
this.setState({
...this.state,
[e.currentTarget.name]: e.currentTarget.value
});
}
}
You can bypass the type checking for your mocked object by using a Type Assertion to assert the type as any:
it('should call the changeHandler', () => {
// Arrange
const changeHandlerMock = jest.fn();
// Act
doStuff(changeHandlerMock, {} as any);
// Assert
expect(changeHandlerMock.called.length).toEqual(1);
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With