When simulating some changes on input with components using hooks, depending of the component, it looks like the onChange is not called.
Whatever the component, when simulating changes on input, the onChange method must be called.
mount
| library | version
| ------------------- | -------
| enzyme | 3.4.1
| react | 16.8.0-alpha.1
| react-dom | 16.8.0-alpha.1
| react-test-renderer | 15.6.2
| adapter (below) | 1.3.0
enzyme-adapter-react-16
import React, { useState, useEffect } from 'react';
import sinon from 'sinon';
import Adapter from 'enzyme-adapter-react-16';
import { mount, configure } from 'enzyme';
configure({ adapter: new Adapter() });
function useFormInput(initial_value = '') {
const [value, setValue] = useState(initial_value);
function handle_change(e) {
setValue(e.target.value);
}
return {
value
, onChange: handle_change
};
}
function Input(props) {
return (
<div>
<input {...props} />
</div>
);
}
function ControlledInputWithEnhancedInput({ searchSomething }) {
const search = useFormInput();
useEffect(
() => { searchSomething(search.value); }
, [search.value]
);
return (
<Input {...search} />
);
}
function ControlledInputWithNativeInput({ searchSomething }) {
const search = useFormInput();
useEffect(
() => { searchSomething(search.value); }
, [search.value]
);
return (
<input {...search} />
);
}
it('does not succeed', () => {
jest.useFakeTimers();
const spy = sinon.spy();
const component = mount(<ControlledInputWithEnhancedInput searchSomething={spy} />);
component.simulate('change', { target: { value: 'foo' } });
jest.runAllTimers();
expect(spy.withArgs('foo').calledOnce).toBe(true);
});
it('does succeed', () => {
jest.useFakeTimers();
const spy = sinon.spy();
const component = mount(<ControlledInputWithNativeInput searchSomething={spy} />);
component.simulate('change', { target: { value: 'foo' } });
jest.runAllTimers();
expect(spy.withArgs('foo').calledOnce).toBe(true);
});
The single fact to wrap the input with a div is hurting the test. Really Weird. Is there anyone having an idea about this?
I don't recommend using simulate at all - it doesn't actually simulate anything.
All you need to test is that a) the onClick prop is present, and b) the function you put there does what it's supposed to. Anything more would be testing React and the browser.
Yes, I know you don't. But that is not my point of view on the matter. I think Kent C. Dodds explained part of my opinion on the matter. However simulate is still part of the api of enzyme. Reporting an unexpected behavior when using it is still making sense, does it?
His opinion on testing (which i highly disagree with) is that you should actually simulate things - but that鈥檚 not what enzyme鈥檚 simulate does, so it鈥檚 irrelevant here. The entire simulate api is unexpected behavior, and i intend to remove it completely as part of a future v4.
enzyme doesn鈥檛 support hooks, and simulate is nothing but sugar over invoking a prop with a fake event object - iow, if you use hooks whatsoever (a currently unreleased feature, but either way, an unsupported one) you should expect enzyme not to work reliably. It鈥檚 best to wait to use React features in components until you鈥檙e able to unit test them - ie, until enzyme supports them.
@ljharb Would you like help with removing it? 馃槂
@k3ithl1m thanks, but i'd prefer not to have a major version right now - when that time rolls around, it'll be pretty straightforward to remove it. A PR to update the docs for it, however, would always be appreciated.
Sure thing. Ill get to it as soon as I can. Just to make sure I'm going towards the right direction.
https://airbnb.io/enzyme/docs/api/ReactWrapper/simulate.html
This is the page that I need to update right? Should we mention in the docs that it shouldnt be used due to unexpected behaviour? or should we just remove it entirely, and anywhere that has simulation, we remove it.
That one, and also the ShallowWrapper one. We definitely shouldn't remove the docs for it, because the method still exists, but we should indicate that they're deprecated, and why.
Well, React 16.8 is out today. I let you decide whether you want to close this issue as I have nothing more to say.
Indeed it is - so as of today, this is a valid issue :-) we don鈥檛 support hooks explicitly, but if you update all of your React package versions, it鈥檚 likely most things will just work - can you confirm you still have this issue? (for example, the shallow renderer and test utils in all of the alphas lacked features that the actual 16.8 has)
I can confirm I still have this issue.
Enzyme probably should either reexport or automatically use ReactTestRenderer.act but otherwise things _should_ just work.
Have same issue - re-writing a component with hooks and simulate doesn't work on a mounted component. Is there another way to test this functionality cleanly?
@ljharb how may I help
@Jessidhia I don't think it should re-export it, but perhaps it should automatically use act - I'm not really sure yet. However, that would only determine console warnings, it wouldn't change how things function, would it?
@k3ithl1m a PR that includes lots of test cases would be great.
@echoes221 fwiw, there's nothing clean about "simulate", that's testing react and the browser - explicitly invoke a prop function if that's what you want to test.
@ljharb OK thanks. I think partly I'm seeing some weirdness with the Fabric component library on the top of it when switching to hooked methods over lifecycle - calling the props directly asserts what I need (It also needs to be wrapped in act btw) but the components don't update appropriately regardless of .update calls.
Thanks for the info. Moving forwards into v4, how would you envision testing this going forwards?
I'm not sure yet; hooks have only been out for a day and a half.
It's likely enzyme will call act for you, sometimes, but I haven't investigated enough to know for sure yet.
@ljharb @ekaradon @chenesan https://codesandbox.io/s/132rz9vq03 added codesandbox where issue is reprodicible even with explicit act call.
@pgangwani that doesn't give me a super helpful error; adding test cases to #2041 or #2029 would be preferred.
Sure, Will add in my next set of test cases. Now focussing on other hooks method.
@ljharb : I have added this test cases in custom hook section in #2041
Just adding in my two cents - it looks like this issue is present with other events such as mouseenter.
Reproduction example
// Component
const Component = () => {
const [hovered, setHovered] = useState(false);
return (
<div style={{ width: 300, height: 300 }} onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)}>
{hovered && <p>This div is hovered</p>}
</div>
);
};
// Test
const wrapper = mount(<Component />);
wrapper.find('div').simulate('mouseenter');
expect(wrapper.exists('p')).toBe(true);
// DOM output after `mouseenter` event (wrapper.debug)
<div onMouseEnter={[Function]} onMouseLeave={[Function]}></div>
(This should be re-evaluated after the next release of enzyme comes out; it may be fixed by then)
(This should be re-evaluated after the next release of enzyme comes out; it may be fixed by then)
@ljharb Curious, what version of enzyme would this be?
Edit: Talked with @ljharb in gitter and got clarity that this is in reference to enzyme 3.10.0
v3.10.0 has now been released.
Closing; happy to reopen if it's still an issue.
As showed here: https://codesandbox.io/s/132rz9vq03 , the issue is still here.
@ekaradon that codesandbox is using enzyme 3.8 and an old react 16 adapter; updating both to the latest, however, shows the same problem. Note that simulate should always be avoided, and wrapping in act is not required when enzyme invokes methods for you.
After fixing linting warnings about useEffect usage, and avoiding simulate, I get: https://codesandbox.io/s/jest-enzyme-react-hooks-sandbox-g9rt1 - which seems to be passing?
Hm. As I was reading that codesandbox, it was using a version equal or greater to 3.8 which was then the latest 3.10, same for the adapter. Or else this is confusing. It's good to know that act is not required. However, if simulate does nothing more than calling the wanted prop, why does it fail?
@ekaradon because you were simulating it on the wrapper and not on the input directly, see https://codesandbox.io/s/jest-enzyme-react-hooks-sandbox-5vjiv
@ljharb
Okay, I see, it works.
Thanks!
Note that avoiding simulate entirely would have surfaced this sooner :-)
Yes, I know you don't. But that is not my point of view on the matter. I think Kent C. Dodds explained part of my opinion on the matter
@ekaradon wrong, you don't have to listen to what Dodds preaches. it's ok to test at a lower level regardless of him telling you not to. You can make the public API either through your browser OR through a set of public functions YOU want to test and if you test it in a smart way who cares if you go through an fing button or not. So what if you don't test button wiring.
Most helpful comment
(This should be re-evaluated after the next release of enzyme comes out; it may be fixed by then)