DEV Community

David Lorenz
David Lorenz

Posted on • Updated on

Don't use CSS classes for testing in Unit Tests or E2E

CSS is meant to style your elements. Sometimes you do that via class and sometimes there is elements that want to be styled the same way all the time e.g. h1.

In my past experience I have seen a lot of projects that write their tests depending on css classes. Although this works it is architecturally not the finest of the finest.

Let's face following problem:

<article class="article">
 <strong class="article__title">Whatever</strong>
 <p>Some text</p>
</article>
Enter fullscreen mode Exit fullscreen mode

Now your task is to test if the "Whatever" is contained.

In your tests you might be doing something like on of these options:

// Option 1:
expect(...query('strong').innerText.trim()).toBe('Whatever');

// Option 2
expect(...query('.article__title').innerText.trim()).toBe('Whatever');
Enter fullscreen mode Exit fullscreen mode

The first option is probably the worst since it means that changing the strong to e.g. em or h2 you would need to adapt that in the test as well.

The second option with querying article__title class is more stable. But also only if you are using this exact semantical BEM approach. If you use something like Tailwind or bootstrap are you then going to search for whatever-xl-highlight class?

โšก๏ธ Both options are not stable for changes!

The stable option is to add semantic probe identifiers.

<article data-probe-id="blogpost">
  <strong data-probe-id="blogpost__title">
Enter fullscreen mode Exit fullscreen mode

Now whatever changes stylewise, removing classes, adding classes, changing class names, the probe-id will stay and make the life of your Teast team easier as it rarely needs to adapt.

Also what comes in handy in Test-Driven-Development is that the Test Team can define the probe-id values upfront before implementation.

Problem solved ๐Ÿš€

Discussion (6)

Collapse
jfbrennan profile image
Jordan Brennan • Edited on

Right on! It's a much better pattern, isn't it?

At my last company our team came up with data-ref="<page_id>.<module_id>.<submodule_id>". Been using it for a couple years now. We use them for all testing and 3rd-party tool integrations (see dev.to/jfbrennan/html-attribute-fo...). So much better than hooking into classes or ids.

Cypress is now recommending a similar pattern too, so I think it's catching on docs.cypress.io/guides/references/...

I've thought about proposing a standard attribute for this purpose.

Collapse
lukeshiru profile image
Luke Shiru

My 2 cents:

If E2E are basically testing user interactions, and you change let's say a strong with a span, shouldn't that give you a test error because you changed a user facing element? I would prefer to have to update the test to match the new element than blindly testing based on the data-probe-id which will not change when the element does. The user doesn't interact with your app based on ids, they interact with your app based on the elements they see on the screen, so if those change the E2E test should too.

Cheers!

Collapse
jfbrennan profile image
Jordan Brennan

I've made the switch to this pattern and it's much much better. Especially for e2e.

Let's say you're testing a page with a list of results and you expect that list to be zero after you filter them with a query that should produce no matches. You don't really care that yesterday the design had them in a <table> and now they're displayed in a <div> using flexbox or something. A good reliable test needs a hook that isn't tied to some design- or application-centric selector.

Collapse
lukeshiru profile image
Luke Shiru

But I'm not talking about design, I'm talking about elements, more specifically semantic elements. If you change the semantics of your WebApp, the tests should give you some kind of error, considering that it will affect user interactions (including a11y), SEO, and so on. A table and a div with flexbox have different meanings, so is ok if the tests fails because it can't find a table, because a screen reader wouldn't find that div either unless you take the time to add all the aria labels, and at that point you should add tests for that as well.

Collapse
alohci profile image
Nicholas Stimpson

Except that's not valid HTML,. You should use data-probe-id instead.

Collapse
activenode profile image
David Lorenz Author

Thanks for the comment. In our company we do use data-probe-id. However even though that is true in actual HTML context it really rarely ever comes with business related downsides not doing so.

If you are all about validity for a specific reason then you could redefine your own doctype :)