kentcdodds / ama

Ask me anything!
https://github.com/kentcdodds/ama/issues?q=is%3Aissue+is%3Aclosed
685 stars 75 forks source link

Strategies on querying elements in integration/E2E tests #513

Closed davincho closed 5 years ago

davincho commented 5 years ago

Hey Kent,

As this is my first question, let me firstly thank you for all your great material you provide to us for free. Especially when it comes to testing, I really like your approaches and ideas how to write better tests that are less prone to break, if implementation details change. I also bought the Pro course on http://testingjavascript.com - great work!

When writing integration tests or E2E tests you recommend to assert on text content, such as placeholder, labels for inputs etc (https://github.com/kentcdodds/dom-testing-library#table-of-contents and https://github.com/kentcdodds/cypress-testing-library).

The people from cypress have the following best practices (https://docs.cypress.io/guides/references/best-practices.html#How-It-Works) image

So my question is: Would you use cy.contains('Submit').click() or cy.get('[data-cy=submit]').click(). Your answer may be "it depends", but do you have examples where approach number 1 would be better than approach number 2 and vice-verse?

Asserting on text

Advantages

Disadvantages

Asserting on data-cy

Advantages

Disadvantages

Cheers

hedgerh commented 5 years ago

I believe the idea behind selecting on text content is that functional tests simulate user flows from a user perspective, so it can be nice to use text content selectors that align with what the user would be seeing in their browser. It's also clearer what you're selecting.

Test does not break if text is updated or dynamic (i18n)

I don't have experience with this, but I imagine you'd be able to lock down the locale and any dynamic data for your tests. Text being updated is a valid one, though.

More stable (?) because data attribute can be considered like an id

Absolutely a benefit to using data attributes. The data-test-id is there strictly for your tests. If you ever changed it, it should be obvious that you'll need to update your tests to use the new id.

Would you use cy.contains('Submit').click() or cy.get('[data-cy=submit]').click(). Your answer may be "it depends", but do you have examples where approach number 1 would be better than approach number 2 and vice-verse?

The data approach would be better when you need to select an element that doesn't contain text. For instance, you need to assert that some element exists on the page.

The text approach may not be good if your text changes a lot, or you need to select on a large string of text?

It's also not the end of the world to use classes or IDs when appropriate, in my opinion. Use whatever works best for you.

Unnecessary additional attribute in the DOM (could be replaced by babel plugins such as https://www.npmjs.com/package/babel-plugin-jsx-remove-data-test-id)

I can't imagine having enough data-test-ids to make this optimization worthwhile.

kentcdodds commented 5 years ago

Hi @davincho 👋

Thanks for the encouragement and for buying testingjavascript.com!

I agree with what @hedgerh has here mostly, though I lean a lot more on the selecting by text. I believe selecting by text is best, so long as you use a regex rather than a string.

Think about the guiding principle behind the *-testing-library tools: The more your tests resemble the way your software is used, the more confidence they can give you.. With this in mind, users are going to look at your text. But users don't care whether the text says "Username" or "username", which is why I suggest using a regex that's specific enough to be helpful, but not so specific to break easily for trivial changes like that.

Now if the content changed from "Username" to "Email", then I actually want my test to break because that would break the user's expectation and we need to have a conversation about the expected change there.

I can't imagine having enough data-test-ids to make this optimization worthwhile.

I agree with this, also having those ids in the prod version of the app is pretty handy for light automated production testing :)

Good luck!