DEV Community

Stefano Magni
Stefano Magni

Posted on

Component vs (UI) Integration vs E2E tests

I'm working on a big UI Testing Best Practices project on GitHub, I share this post to spread it and have direct feedback.


Speaking about UI Testing (remember that we are speaking about the UI only, not the underlying JavaScript code), there are three main test types:

  • Component tests
  • UI Integration tests
  • End-to-end (E2E) tests

Component tests

The unit tests of a UI, they test every single component in an isolated
environment.

Developing components in isolation is important because it allows you to isolate them from the corresponding container/use. A component exists to isolate a single behavior/content (the Single responsibility principle) and therefore, coding it in isolation is profitable.

There are many ways and tools to develop components in isolation but
Storybook became the standard choice because of its effectiveness and its ecosystem.

Components have three types of contracts: the generated output (HTML), their visual aspect (CSS), and the external APIs (props and callbacks).
Testing every aspect could be cumbersome, that's where Storyshots comes in
handy. It allows you to automate:

the snapshot tests: a snapshot is an output generated by your component, a string containing all the rendered HTML. If the generated HTML changes,
accidentally or not, the snapshot tests fail and you can choose if the changes were intentional or not.

the visual regression tests: the visual aspect of the component compared pixel by pixel with the previous one, again, you are prompted to choose if you accept the changes or not.

These tests are launched by Storyshots automatically on every Storybook page (AKA "stories").

the callback tests: a small React container app renders the component passing it some callbacks. Then, the user interactions are simulated and passed the callback is expected to be called.
React Testing Library is the standard library of choice for this kind of tests

the interaction/state tests: some interactions with a component expect correct state management. This kind of test must be written from the consumer point of view, not from the inner one (ex. the value of the input field when the user fills it, not the inner component state). An interaction/state test should assert the input field value after the keyboard events triggered.

UI Integration tests

They run the whole app in a real browser without hitting a real server.
These tests are the ace in the hole of every front-end developer. They are blazing fast and less exposed to random failures or false negatives. Cypress is perfect for UI Integration tests.

The front-end application does not know that there is not a real server: every AJAX call is resolved in no time by the testing tool. Static JSON responses (called "fixtures") are used to simulate the server responses. Fixtures allow us to test the front-end state simulating every possible back-end state.

Another interesting effect: Fixtures allow you to work without a working back-end application. You can think about UI Integration tests as "front-end-only tests".

At the core of the most successful test suites, there is a lot of UI Integration tests, considering the best type of test for your front-end app.

End-to-end (E2E) tests

They run the whole app interacting with the real server. From the user
interactions (one of the "end") to the business data (the other "end"): everything must work as designed. E2E tests are typically slow because

• they need a working back-end application, typically launched alongside the front-end application. You can't launch them without a server, so you depend on the back-end developers to work

• they need reliable data, seeding and cleaning it before every test

That's why E2E tests are not feasible to be used as the only/main test type. They are pretty important because they are testing everything (front-end + back-end) but they must be used carefully to avoid brittle and hour-long test suites.

In a complete suite with a lot of UI Integration tests, you can think about E2E tests as "back-end tests". What flows should you test through them?

• the Happy Path flows: you need to be sure that, at least, the users are able to complete the basic operations

• everything valuable for your business: happy path or not, test whatever your business cares about (prioritizing them, obviously)

• everything that breaks often: weak areas of the system must be monitored too

Identifying/defining the type of test is useful to group them, to limit their scope, and to choose where to run them or not though the whole application and deployment pipelines.

Again, Cypress is my tool of choice for E2E tests.

Name them wisely

You can write a lot of different UI tests and it's a good habit to have a common way of naming the test files.

It's useful because often you need to run just a type of tests, the situations could be:

  • during the development process, you need to run just some of them
    • you're changing some related components and you need to check that the generated markup does not change
    • you're changing a global CSS rule and you need to run only the visual tests
    • you're changing an app flow and you need to run the whole app integration tests
  • your DevOps colleague needs to be sure that everything is up and running and the easiest way to do that is launching just the E2E tests
  • your building pipeline needs to run just the integration and E2E tests
  • your monitoring pipeline needs a script to launch the E2E and monitoring tests

If you name your tests wisely, it will be really easy to launch just some kind of them.

Cypress:

cypress run --spec \"cypress/integration/**/*.e2e.*\"
Enter fullscreen mode Exit fullscreen mode

Jest:

jest --testPathPattern=e2e\\.*$
Enter fullscreen mode Exit fullscreen mode

A global way to name the test files does not exist, a suggestion could be to name them with:

  • the subject you are testing
  • the kind of test (integration, e2e, monitoring, component, etc.)
  • the test suffix of choice (test, spec, etc.)
  • the file extension (.js, .ts, .jsx, .tsx, etc.)

all of them separated by a period.

Some examples could be

  • authentication.e2e.test.ts
  • authentication.integration.test.ts
  • site.monitoring.test.js
  • login.component.test.tsx etc.

Top comments (10)

Collapse
 
omril321 profile image
Omri Lavi

Another great article, thank you!
From your experience, what are the advantages of using StoryShots over Chromatic tests?
Also, what are your opinions about using Playwright instead of Cypress? Playwright has become quite popular, and it seems much faster than Cypress. Furthermore, it has access to browser APIs that Cypress can't access, like memory usage and network requests. I'm interested to hear your thoughts about this :)

Collapse
 
noriste profile image
Stefano Magni

Hey Omri!
Consider that the post is quite old and the content is not update 😊

So I would go for Chromatic tests nowadays :) But not for the snapshot tests themselves (I shared here what I think about them and I do not suggest snapshot testing anymore) but for the component tests that's way more powerful, ha you already used them?

Also, what are your opinions about using Playwright instead of Cypress?

Playwright is way faster, it's more stable, and its UI mode is great! Anyway, what you see in the UI mode is not the live application but a series of snapshots, that means you can debug the HML+CSS but not the JS state, instead Cypress can. The day they Playwright fixes this problem I will jump immediately on it 😊 Playwright is great!!!

Anyway, I still feel the lack of a service like the Cypress Dashboard but it's just a matter of time or external SaaS 😊

What are your thougths about the topic? Which one you use?

Collapse
 
omril321 profile image
Omri Lavi • Edited

Hey Stefano! :)
I actually didn't notice the publish date 😅

I would go for Chromatic tests nowadays :) But not for the snapshot tests themselves

So if I get it right, you recommend Chromatic tests for the interactions + screenshots, but nothing more. Yes?
If I got you right, then it's pretty much what we do as well. We use a lot of Chromatic interaction tests in our components library (named Vibe). It works quite well for us, although it has some limitations (like the inability to write test suites).
Do you use Chromatic interaction tests a lot?

that means you can debug the HML+CSS but not the JS state, instead Cypress can

I wasn't aware of the difference between Cypress and Playwright in regards to JS debuggability, that's quite interesting. Currently we rely heavily on Cypress, but we would have wanted to shift to Playwright at some point. In addition to the advanced features that Playwright has, it can also run in parallel on AWS Lambda (for example). This can be very useful if you run many tests. Have you ever tried something like this?

Thread Thread
 
noriste profile image
Stefano Magni

So if I get it right, you recommend Chromatic tests for the interactions + screenshots, but nothing more. Yes?

Yes, today I suggest this

although it has some limitations (like the inability to write test suites).

To be 100% sure, could you explain me what you mean with "test suites"? (sorry but in the testing world we all use different terms for different things, me included :D )

I wasn't aware of the difference between Cypress and Playwright in regards to JS debuggability, that's quite interesting.

Maybe because it's not crucial for you and your devs 😊 you could do a small Playwright-migration POC, I think you won't regret it 😊

Thread Thread
 
omril321 profile image
Omri Lavi

To be 100% sure, could you explain me what you mean with "test suites"?

Sure! I'm probably using the wrong terms myself 😅 When I refer to Chromatic tests, I think of Chromatic's interaction tests. In these tests, you implement a play function on your story, which interacts with it (type, click, etc) and assert on it. The thing is, that if you wish to have two tests on the same story (what I think of a "test suite"), you can't. Are you familiar with these tests? If so, was your experience similar? If not, how do you test your stories?

you could do a small Playwright-migration POC

You can rest assure that it's in our backlog 🥲

Thread Thread
 
noriste profile image
Stefano Magni

The thing is, that if you wish to have two tests on the same story (what I think of a "test suite"), you can't. Are you familiar with these tests? If so, was your experience similar? If not, how do you test your stories?

The question is: why do you need two tests for a single story? Think about it from the readers' perspective: a story tells something, and the test is just part of that story.

What I do is

  1. Every test has a dedicated story
  2. Every dedicated story has just one test

As an example, look at this story/test. If you look at the sidebar, you can find that there are two stories that have been created just to write the tests. So, one test = one story. Storybook is a great tool to communicate to other devs and stakeholders what the component does and what you tested. What do you think?

Thread Thread
 
omril321 profile image
Omri Lavi • Edited

Writing one story per one test is an interesting approach. I can see the benefits. However, please consider a case of a more complex component. For instance a dropdown with a search input. There are numerous use cases that should be tested (search, clearing the input with the "X" button, many cases of accessibility). If we'll write a story for each single test, we can easily end up with 10 or more identical stories. I find this problematic for a two important reasons:

  1. Code duplication - updating the component in a way that requires updating the story, will make you update 10 or more identical stories.
  2. Non-navigable stories - instead of having a single story, you get 10+ stories that are identical. Multiplying this by the number of components you write stories to, you can end up with an extremely bloated storybook, which can't be navigated with ease. Note that this can be solved with proper conventions and filtering (e.g. prefixing test-stories with "Testing", like the example you sent). However I believe it still adds a lot of noise.

I agree that this approach has many benefits as well. I just wish there was a way to write interaction tests in a similar way to how you write unittests: you declare a suite with some tests in it, and can use the before/after hooks for any setup you need. Then, you can run any slices of the suite as you need. When Chromatic integration tests have this support, I will be a happy developer 😊

WDYT?

Thread Thread
 
noriste profile image
Stefano Magni

WDYT?

I agree, the choice should be on you, not on the platform you use 😊

Collapse
 
heyitsyuliya profile image
Yuliya K

Great read! I’m using Cypress in conjunction with Cucumber Preprocessor plugin and found it super useful tagging my tests. Cypress will pick up all tests in .feature file that are tagged with @e2e. Also, tags come in handy when you’re developing your tests and don’t want to run the whole feature file.

Collapse
 
noriste profile image
Stefano Magni

Thanks for sharing Yuliya!! Cucumber is going to be the next thing to study for me 😊