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

Mocking arrow function refs in shallow, messed up by updates. #1407

Closed
AndrewSouthpaw opened this issue Dec 2, 2017 · 15 comments
Closed

Mocking arrow function refs in shallow, messed up by updates. #1407

AndrewSouthpaw opened this issue Dec 2, 2017 · 15 comments

Comments

@AndrewSouthpaw
Copy link

Note: I'm using enzyme 2.9.1.

Because of the nature of some of our components, we need to use refs. React calls for us to create those with arrow functions, which enzyme shallow in turn can not figure out. If I have something like...

export class PresentLessonModal extends React.PureComponent {
  handleSave = () => {
    this.modal.close()
    // ...
  }
  
  render() {
    return (
      <Modal ref={e => this.modal = e}>
        {/* ... */}
      </Modal>
    )
  }
}

the this.modal is undefined.

I've taken to doing PresentLessonModal.prototype.modal = myMockModal in my tests, but subsequent updates to the wrapper, e.g. with setState or update, makes the ref undefined again. Any input on how to do this properly?

@ljharb
Copy link
Member

ljharb commented Dec 2, 2017

The issue here is that you shouldn't be using arrow functions inside class properties. Instead, you should be using a constructor-bound (or class-property-bound) instance method, like this:

handleSave = this.handleSave.bind(this);
handleSave() {
  this.modal.close();
  
}

Additionally, the ref callback you use needs to be the same reference every render - ie:

setModal = this.setModal.bind(this);
setModal(e) {
  this.modal = e;
}


<Modal ref={this.setModal}>

@hally9k
Copy link

hally9k commented Dec 6, 2017

@ljharb Could you elaborate on "you shouldn't be using arrow functions inside class properties". Is that an enzyme thing or a react thing or a javascript thing? Or is this just general wisdom that I have yet to grok?

@AndrewSouthpaw
Copy link
Author

AndrewSouthpaw commented Dec 6, 2017

@hally9k Read #697 for a very long discussion about the topic.

TL;DR: some, such as @ljharb, argue that not using arrow functions inside class properties is better for a number of reasons (i.e. it's a JS thing), including that it's easier to mock methods and refs on the component. Many others are displeased with that stance, and a lengthy discourse about performance and redundant code ensued.

My personal takeaway: I quite like arrow functions inside class properties despite the many reasonable arguments against it (that, and it's how our entire codebase does it), and will selectively adopt the approach suggested above when necessary for testing purposes.

@ljharb
Copy link
Member

ljharb commented Dec 6, 2017

"There's tons of reasonable arguments against it, but we already do it this way and I like it" is, at the least, a totally valid and honest defense :-D

@AndrewSouthpaw
Copy link
Author

Better than getting vitriolic. 😉

Thanks for the quick response, btw. I don't know how you field so many questions on so many repos, but it makes a big difference to figuring out the finer points of some of these libraries.

@hally9k
Copy link

hally9k commented Dec 6, 2017

@AndrewSouthpaw Sorry, I got pulled off into an interview. Thanks for the link. I missed your comment and was already on the other thread leaving my 2 cents. I don't want to go and open a can of worms that everyone is tired of (That thread is quite long) but I do think the reasoning (and support) is a little lop sided. Keen to hear some solid reasoning. Also I struggled to get consistent behaviour even with the work arounds so I'd like to learn more. Any tips on using enzyme @ljharb? (other than not use class property arrow functions of course 😉 )

@ljharb
Copy link
Member

ljharb commented Dec 6, 2017

@hally9k to your earlier question, it's a React thing and a JavaScript thing and a testing thing, all together.

General tips? Lean on shallow, and shallow-render every component in tests; use .dive() for each HOC in use. Use mount where necessary. Avoid simulate; explicitly invoke prop functions instead.

@AndrewSouthpaw
Copy link
Author

AndrewSouthpaw commented Dec 6, 2017

Avoid simulate

That's a new tip for me. I heard some people saying it's better to interact with the DOM elements through simulate than knowing what the prop functions are called. Can you expand on your side?

@AndrewSouthpaw
Copy link
Author

@hally9k Also for your reference, mocking refs for me works as long as I use the same ref callback each time. i.e.

setRef = (e) => (this.modal = e)
...
<Modal ref={this.setRef}>

works fine, the don't-use-arrow-functions-in-class-properties is not an issue in this case.

What's the inconsistent behavior you're encountering?

@rodoabad
Copy link

@ljharb is there a reason why you're recommending avoiding simulate?

@AndrewSouthpaw
Copy link
Author

Yeah, I'm moving toward simulate and it's cousins, rather than leaning more heavily on invoking prop functions. But I think that's because I am migrating more toward testing user behavior as much as possible, rather than implementation details. (This also means writing more integration tests than unit tests.)

I'd be curious to hear about @ljharb's reason for avoiding simulate though.

Philosophical discussions are fun. 😉

@rtymchyk
Copy link

I lean towards shallow rendering, but that sometimes makes it impossible to use event simulation.

@rodoabad
Copy link

@AndrewSouthpaw here's another excerpt on my philosophy regarding testing the behaviour of the component and not it's implementation details.

@ljharb
Copy link
Member

ljharb commented Jun 28, 2018

I'd avoid simulate because it doesn't actually faithfully simulate anything. If you want to invoke a prop, invoke it directly.

@ljharb
Copy link
Member

ljharb commented Jul 7, 2018

This is answered: never put arrow functions in class fields, and you won't have any trouble.

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

5 participants