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

Deprecation of .simulate() #2173

Open
browne0 opened this issue Jun 25, 2019 · 10 comments
Open

Deprecation of .simulate() #2173

browne0 opened this issue Jun 25, 2019 · 10 comments

Comments

@browne0
Copy link

browne0 commented Jun 25, 2019

Hello! Big fan of enzyme here.

I use Enzyme a lot for unit testing and I've noticed recently that people are consistently recommending people use instance prop functions versus using the simulate behavior provided by Enzyme because of the bad implementation.

Is there anyway we could get the docs updated with what the preferred solution is? It's not really clear as the docs stand right now.

Can we also add a note that there are plans to deprecate it Enzyme v4 too? I saw that in the comments and I was surprised by it. I'm more than happy to put in a PR if that is the preferred solution.

Cheers.

@browne0 browne0 changed the title Deprecation of .simulate Deprecation of .simulate() Jun 25, 2019
@ljharb
Copy link
Member

ljharb commented Jun 25, 2019

simulate is indeed deprecated; if there's ever a v4 of enzyme it will be removed with prejudice.

The reason it's bad is because it's named "simulate" and it takes an event name, but it does not actually faithfully simulate anything. It's based on the historic implementation of the same method in the shallow renderer which suffers from the same problems. All it does is transform the event name to a prop name, and invoke the function in that prop.

The only way a replacement would be worth considering is if it actually simulated the event - ie, simulating "change" produced a stream of events, including keypress/keydown/keyup/etc, input, change, blur, whatever made sense - and invoked all the expected handlers as a result. This is a very large project, however, and I don't think the knowledge even exists in a single package in the entire ecosystem yet to encapsulate it (it's only in browsers, and not something that could be referenced in this direction).

A PR to make this clear in the docs would be welcomed.

@browne0
Copy link
Author

browne0 commented Jul 1, 2019

Awesome, I'll get started on a PR today.

@browne0
Copy link
Author

browne0 commented Jul 3, 2019

@ljharb not sure if you saw but I opened a PR on Monday.

@ljharb
Copy link
Member

ljharb commented Jul 3, 2019

I saw; I’ll take a look as soon as i can - thanks!

@azangru
Copy link

azangru commented Jul 15, 2019

All it does is transform the event name to a prop name, and invoke the function in that prop.

@ljharb with deprecation of .simulate(), are you planning to introduce some kind of a convenience method to dispatch events on a DOM element (like what react-testing-library does with their fireEvent function)?

If not, what would be an idiomatic way to test component's behavior in response to various DOM interactions? I am aware that you suggest directly accessing a callback functions passed as props to a component and calling them; but if we stick to the principle of testing public api of a component and not peeking inside, what will a test of a DOM-mounted component look like?

@ljharb
Copy link
Member

ljharb commented Jul 16, 2019

@azangru i'm not convinced any convenience method is needed - if you want to invoke the onClick prop, invoke it.

You shouldn't be testing the browser or React itself - and anything related to events is doing just that. Shallow testing is all about peeking inside - you're supposed to test that.

@azangru
Copy link

azangru commented Jul 16, 2019

if you want to invoke the onClick prop, invoke it.

Shallow testing is all about peeking inside - you're supposed to test that.

@ljharb I am sorry; I must have expressed myself poorly. Let me try again.

As far as I understand, there are two testing strategies when testing React components.

One is testing a component entirely in isolation. This is purely a unit test and is probably best achieved with shallow rendering. As you say, with shallow rendering you are supposed to peek inside a component, reach for props that you pass to children components, and use them to test the component's functionality. Since children components are not rendered anyway during a shallow test, that's the only practical way of testing callbacks passed to children components.

Of course the drawbacks of shallow testing is that it relies too much on the implementation of React the library; so when React extended its api with hooks, shallow testing was not yet ready for that.

Another strategy is to test the component as well as the whole tree of its children by fully rendering it to the DOM. This is where a unit test borders on (or perhaps becomes) an integration test. This strategy is evangelized by react-testing-library. Using this strategy, you are interacting with the component through the DOM, "as the user would". Which means that to fire a callback in the component you are testing, you want to find the appropriate DOM element and dispatch an event on it.

This second strategy can be achieved in enzyme with full DOM rendering, which would make it possible to use enzyme in the same manner one would use react-testing-library, but still having enzyme's flexible api at one's disposal. It would be very handy, for that kind of testing, to have a convenience method for firing DOM events. I appreciate that such a method will make zero sense for shallow testing; but will make a whole lot of sense for full DOM testing.

@ljharb
Copy link
Member

ljharb commented Jul 17, 2019

@azangru mount's simulate already uses React's TestUtils simulate method, so I believe that's already basically how it operates. I also agree with your description of these two strategies (there are more than two) - but your second strategy is both a) not reliable using jsdom, so you'd need to do it in an actual browser to test properly, so that's where something webdriver-esque should come in; and b) not sufficient to cover server-rendering, which is very important for a11y and robustness.

Separately, you can always find an HTML element on a mount wrapper, and element.getDOMNode().dispatchEvent(…) directly - does that not meet the need you described?

@azangru
Copy link

azangru commented Jul 17, 2019

@ljharb thank you for your explanation; I wasn’t aware how mount’s simulate works. Just to clarify though, when you talk of deprecating simulate, do you mean this both for ShallowWrapper and ReactWrapper, as now stated in the docs updated in the PR linked to this issue? I completely understand the reasoning behind removing simulate from ShallowWrapper; but since, as you say, ReactWrapper’s simulate is a proxy to React’s TestUtils’ simulate, doesn’t it justify keeping simulate on ReactWrapper?

@ljharb
Copy link
Member

ljharb commented Jul 17, 2019

@azangru v4 decisions haven't been made since v4 is a long way off. In general, I want to keep as close parity as possible between mount and shallow's methods. I agree that the current functionality of mount's simulate might be worth keeping, but i'd probably remove it and add a simulateEvent method, but with more restrictions on what you can call it on - and then i'd want to try to figure out how it could be properly added for shallow.

Vanguard90 added a commit to Vanguard90/cheatsheets that referenced this issue Sep 12, 2019
`.simulate` will be depreciated in the future. Adding alternate methods to the cheatsheet will make it more complete. See enzymejs/enzyme#2173
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants