Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testing Hooks with mount: onChange not called #1996

Closed
ekaradon opened this issue Feb 1, 2019 · 32 comments
Closed

Testing Hooks with mount: onChange not called #1996

ekaradon opened this issue Feb 1, 2019 · 32 comments

Comments

@ekaradon
Copy link

ekaradon commented Feb 1, 2019

Current behavior

When simulating some changes on input with components using hooks, depending of the component, it looks like the onChange is not called.

Expected behavior

Whatever the component, when simulating changes on input, the onChange method must be called.

Your environment

API

mount

Version

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

Adapter

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?

@ljharb
Copy link
Member

ljharb commented Feb 1, 2019

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.

@ekaradon
Copy link
Author

ekaradon commented Feb 2, 2019

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?

@ljharb
Copy link
Member

ljharb commented Feb 2, 2019

His opinion on testing (which i highly disagree with) is that you should actually simulate things - but that’s not what enzyme’s simulate does, so it’s irrelevant here. The entire simulate api is unexpected behavior, and i intend to remove it completely as part of a future v4.

enzyme doesn’t 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’s best to wait to use React features in components until you’re able to unit test them - ie, until enzyme supports them.

@kii-dot
Copy link
Contributor

kii-dot commented Feb 5, 2019

@ljharb Would you like help with removing it? 😃

@ljharb
Copy link
Member

ljharb commented Feb 5, 2019

@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.

@kii-dot
Copy link
Contributor

kii-dot commented Feb 6, 2019

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.

@ljharb
Copy link
Member

ljharb commented Feb 6, 2019

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.

@ekaradon
Copy link
Author

ekaradon commented Feb 6, 2019

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.

@ljharb
Copy link
Member

ljharb commented Feb 6, 2019

Indeed it is - so as of today, this is a valid issue :-) we don’t support hooks explicitly, but if you update all of your React package versions, it’s 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)

@chenesan chenesan mentioned this issue Feb 7, 2019
7 tasks
@ekaradon
Copy link
Author

ekaradon commented Feb 7, 2019

I can confirm I still have this issue.

@Jessidhia
Copy link

Enzyme probably should either reexport or automatically use ReactTestRenderer.act but otherwise things should just work.

@calumpeak
Copy link

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?

@kii-dot
Copy link
Contributor

kii-dot commented Feb 7, 2019

@ljharb how may I help

@ljharb
Copy link
Member

ljharb commented Feb 7, 2019

@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.

@ljharb
Copy link
Member

ljharb commented Feb 7, 2019

@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.

@calumpeak
Copy link

@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?

@ljharb
Copy link
Member

ljharb commented Feb 8, 2019

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.

@pgangwani
Copy link

@ljharb @ekaradon @chenesan https://codesandbox.io/s/132rz9vq03 added codesandbox where issue is reprodicible even with explicit act call.

@ljharb
Copy link
Member

ljharb commented Mar 10, 2019

@pgangwani that doesn't give me a super helpful error; adding test cases to #2041 or #2029 would be preferred.

@pgangwani
Copy link

Sure, Will add in my next set of test cases. Now focussing on other hooks method.

@pgangwani
Copy link

@ljharb : I have added this test cases in custom hook section in #2041

@andyrichardson
Copy link

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>

@ljharb
Copy link
Member

ljharb commented Apr 24, 2019

(This should be re-evaluated after the next release of enzyme comes out; it may be fixed by then)

@bjankord
Copy link

bjankord commented May 21, 2019

(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

@ljharb
Copy link
Member

ljharb commented Jun 4, 2019

v3.10.0 has now been released.

Closing; happy to reopen if it's still an issue.

@ljharb ljharb closed this as completed Jun 4, 2019
@ekaradon

This comment has been minimized.

@ljharb

This comment has been minimized.

@ekaradon

This comment has been minimized.

@ljharb

This comment has been minimized.

@ekaradon

This comment has been minimized.

@ljharb

This comment has been minimized.

@dschinkel
Copy link

dschinkel commented Oct 3, 2020

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants