Zen and the Art of UI Components Testing

Rate this content
Bookmark

Yes, we do need to test our UI components but... If this rings a bell, and especially if your application has advanced UI functionality, this talk is for you.In this talk, we will cover what are the factors that need to be tested in UI components. We will challenge the testing pyramid when it comes to UI Components testing, and review the different tools that we have nowadays for making UI component testing complete Zen.

21 min
11 Dec, 2023

Video Summary and Transcription

The Talk discusses the evolution of test automation from the original test automation pyramid to the testing pyramid. It explores modern approaches to UI component testing, including isolations and testing with a fake DOM. The importance of testing in a real browser and the emergence of tools like Selenium, WebDriver.io, Puppeteer, Cypress, and PlayWrite for browser automation are highlighted. The advantages of out-of-process browser automation are explained, along with the use of Storybook and Playwright for testing components. The distinction between end-to-end testing and component testing is also mentioned.

Available in Español

1. Introduction to Test Automation Pyramid

Short description:

In 1990, testing was not taught in programming boot camps. A book published by Mycom introduced the original test automation pyramid with three levels: unit, service, and UI. Over time, it evolved into the testing pyramid, with unit tests, integration tests, and end-to-end tests.

A short walk down memory lane. This is me. Here I just graduated my programming boot which was in the Israeli Army, the Mammran boot camp. We learned a lot of things. The one thing we did not learn is testing. And this is because the year is 1990. And this is the very same year that this book was published by Mycom. And it's talking about succeeding with Agile. And if you dive into this book, you would find this diagram. This is the original test automation pyramid. It talks about three levels. Unit, service, and UI. And you notice that the UI is at the top. Later on, this is evolved into the testing pyramid, which we all know. And the name changed. We still talk about unit test. But we also talk about integration. And the UI test has become end to end test. And this is actually true, because this is what happened for many years. We would see end to end test as a synonym for UI tests.

2. Modern Approaches to UI Component Testing

Short description:

I want to talk about modern approaches to UI component testing. The first principle is isolations. Components let you split the UI into independent and reusable pieces. You can think about each piece in isolation, build it in isolation, and test it in isolation.

My name is Tali Barak. I work for a company called Ubiq. And I want to talk about modern approaches to UI component testing, and I call this talk Zen and the Art of UI Component Testing. And soon, we will see what is the Zen and how we can achieve it.

And the first thing, the first principle, is I want to talk about isolations. This is a page, what a web page, a web application looked like back in the 1990s. As you can see, it is quite simple and it is just one page. But then, later on, and we're talking here about 2015, sort of like, we started working in a complete different way when building our web application. We started talking about components.

This is AngularJS, as I said, in December 2015. They are talking about understanding components when you develop an Angular application. And this is from June 2016. This is the actual commit that was made into the React readme. And it started talking also about what it means to be component based. It talks about building encapsulated components that manage their own state and then compose them into more complex UIs. And we still look at components as sort of lego bricks. You have a separate component exhibit. Each one with its own functionality and then you go and you build them into larger UI such as ships, houses, or even a search engine rack server that was built from lego.

Today when we talk about the modern framework for building UI such as Angular React, Vue, Solid, Svelte, etc., they are all built on the principle of component-based. And this is what components let you do. They let you to split the UI into independent and reusable pieces and you can think about each piece in isolation. And when you say think, it's not just about thinking, it's also about building it in isolation and testing it in isolation. In this example here, we can see this is a CodeWe app, a demo app that is built in multiple languages and frameworks. You can just find almost any framework you would like. And you can see here that it is actually using the same component of an article preview both on the global feed and on the MyPost. And this actually implies that if I want to test the functionality of this component, I don't need to go to the specific page of the global feed as I would do in end-to-end test or to login and go into MyPost as I would do in the MyPost page. I can actually isolate this component, put it on separately. And such as we see here, this is the same component, but the page only shows this component. This is using Storybook. Storybook is a great tool for demoing and showing and showcasing components in isolation.

3. Testing UI Components with a Fake DOM

Short description:

I can test my component in isolation. When testing UI components, we use tools like Jest or V-test, which work with a fake DOM. This fake DOM is created using engines like Node.js, but it doesn't fully replicate the browser's behavior. Therefore, when testing components, it feels like playing a guessing game, as we can't see the actual visual side. We rely on tools like enzyme or testing library to assist us.

I can see here this is from the same application. I took the editor component. This is when I want to compose an article. And you can see on the same Storybook, I have this component in isolation. And once I have this component, this is zooming in into the component, I can actually test that it works correctly, regardless of which page it is put on. So I can check here that I have my likes, that I can click on them. I can check that the read is actually the reference to the correct URL. I can test here that I'm showing the correct tags, that they are in the right order if there is a specific rule for the order and so on. So point number one is I can test my component in isolation.

The other thing when I talk to people about UI components, how do you test them? They would usually say something. Yes, I want to test the data, I want to test the user interaction, I want to test the layout, I want to test that it raises event and they would use tools like Jest or V-test to test my components. What is important to understand when we are using Jest is that usually we are using not a real browser but we are using what is called a DOM mock. The one at the top is very popular. There is also a newer one that is called HappyDOM. What does it mean working with a fake DOM? When we are building our web application, regardless of component or not, under the hood, it still needs to call the DOM, the document object model, which is the core of how the browser works. And every time we build a component, and it doesn't really matter which frameworks we use, at the end of the day, it will need to call the DOM APIs in order to render the component. We might wrap it in some JSX or TSX or Angular templates or whatever, but under the hood, it still needs to be translated to query selector, create element, set attribute, add event listener, and all the API that we are familiar on the DOM. And usually these APIs are provided by our browser. The browser conforms to the standard of what the DOM APIs work like and we would use it. But what people decide to do with a fake DOM is to actually take other engines that can run JavaScript, and mostly this is Node.js. We have some other engines nowadays, but Node.js is definitely the most popular one. And we build all the APIs that the browser has but in Node.js. But the important thing to understand is yes, we fake the APIs, but we don't really do what the browser does. And this mostly means we are not rendering really the component because this is still fake. It doesn't know how to render components. And what people say very often is that when you're testing your component this way, it feels a bit like playing what's in the box. Because the component, this is a UI component, it has a visual side, but we are not really seeing it. So we are just trying, when we are testing, trying to guess what is inside. We are using tools like enzyme or testing library and so on. But still, this is a lot of guessing game.

4. UI Component Testing and Browser Automation

Short description:

When working with advanced UI components, it can be challenging to mock APIs and create a fake DOM that accurately simulates a browser. Testing in a real browser is essential to ensure proper rendering, CSS functionality, and interaction testing. Over the years, various tools have emerged to support browser automation, including Selenium, WebDriver.io, Puppeteer, Cypress, and PlayWrite. Testing in a real browser and achieving efficiency are two key goals in component testing.

And especially when we are working with advanced UI components, UI components that have things like advanced media that have canvases and graphs that is being animated. And we have a lot of micro-interactions and we also have a lot of gestures that we need to support, such as drag and drop or zooming and so on. This is where the mocking of the APIs are played really short. And it's sometimes very hard and you're just trying to make your fake DOM, fake browser work like a browser and you invest a lot of time and it's really frustrating in the test.

Because at the end of the day, it's very simple. When we have UI components, we want to test them in real browser. And this means that it will really render our component. We are not just going to test that we have the right CSS class, but we also want to test that the CSS is building our component correctly and it's actually correctly visualized. We want to talk about network call that is sometimes different when we are working with browser. We want to test the interaction, all the gestures that I mentioned, and also proper timing. When we are working with a mock, it usually does not conform to the timing that the browser really has, or pseudo event and so on. If there are side effects, again, pseudo event is one example, like input change and so on, we want to make sure that it's really triggered correctly, and not us manually triggering, and at the end of the day we might want to actually look at the component and screenshot it and do a visual regression test and make sure that it looks just like we expected it to look.

And over the years, there are a lot of tools that are supporting this kind of browser automation. They usually are a synonym of end-to-end tests, again from this approach, it's saying UI is only being tested in end-to-end. We have Selenium for 20 years now, we have WebDriver.io which is very popular. These are not the only ones. And recently, in the last few years, we have more modern tools like Puppeteer and Cypress, and just probably the latest and greatest addition is PlayWrite together with the newer version of Selenium. And we will discuss how they are different.

So, in order to achieve our second endpoint, we want to make sure that we test in a real browser. The third point for making this testing zen and fun, we want it to be efficient. We want to make sure that we write the test and that they run fast and easy to change and easy to do all the things that we need on top of them. And in order to understand how we can achieve efficiency, let's zoom a bit into the browser. This is what we know as the browser, but this is actually the rendering process of the browser. This is what we see on the screen. It has JavaScript, and now it has also workers. But under the hood, the browser has a lot of other processes like the one that you see here. We have the service worker process. We have network process. We have all the browser processes, things that control security, location, the inputs, getting the input from the operation system and sending it to the browser, and a lot of other parts. The first generation, or what was common in order to test the processes, was to actually run in the JavaScript process inside the browser.

5. Out-of-Process Browser Automation

Short description:

In this way, you could run your test in JavaScript, but everything else was not accessible to you. The second generation is those fake browsers that I talked about, which are still popular. The more modern approach is to have an out-of-process automation. What do we get by out-of-process browser automation? Why is it so good? First of all, we have full access to the file system because this is external and not running on the browser. Cross-language, it doesn't have to be JavaScript. We have full control because we are accessing via the API. We are not limited to just one window or one tab where our tests are running. So, what does that mean for our ZenUI component testing? This is what we are doing at ubiq. We are using a combination of playwright and storybook to test our component.

In this way, you could run your test in JavaScript, but everything else was not accessible to you. You could not do a real input, you could not impact location or security, and so on.

The second generation is those fake browsers that I talked about, which are still popular, which basically say we don't have any of this, we are just writing in Node some of the test that we want. And the more modern approach is to have, what we call, an out-of-process automation. All of this part of the browser is actually accessible via APIs. It started as a Chrome developer protocol. And we write an external program that is actually controlling all of these part. This was initiated with Puppeteer in Google. This is what they did as a generic tool that is written in Node.js and it's controlling our browser through this protocol. And later on, the same team that was building Puppeteer in Google has moved to Microsoft and built Playwright. And they extended this protocol, not just for generic browser automation, but actually focusing on what is available, what is required for testing, such as if we have animation, we want to wait until it stabilizes and only then I want to click the button. A lot of the problems existed with all tools like Selenium.

What do we get by out-of-process browser automation? Why is it so good? First of all, we have full access to the file system because this is external and not running on the browser. This is running on our machine. For example in Node.js, we can access any file. We can access the test wherever we want. We can run in parallel. Again, this is a separate program. We don't have any problem running it multiple times as many as needed. Cross-language, it doesn't have to be JavaScript. This automation that is controlling our test can be written basically in any language. You can see that in Playwright, it has Python, .NET, and more. We have full control because we are accessing via the API. We have full control to the security, to the input, to the location services, even to the performance file, and so on. The interactions are real because we're, again, calling these APIs. We are passing interaction the same way that our operating system is sending them. We are not limited to just one window or one tab where our tests are running. We can spawn as many windows and tabs as we want and run the test on top of them.

So, saying all of that, what does that mean for our ZenUI component testing? This is what we are doing at ubiq. We are using a combination of playwright and storybook, as I gave some hints along the way, in order to test our component.

6. Testing Components with Storybook and Playwright

Short description:

We use Storybook to build and render our components in isolation. Playwright is the tool we use for browser automation and testing. With Playwright, we can achieve real component rendering without the need for mocking. It provides stability, efficient execution, and the ability to run tests in parallel and headless. We can also perform visual regression tests and take screenshots. Storybook and Playwright are continuously improving their testing capabilities, including component testing. End-to-end testing involves working with a real backend server and testing complete user flows, while component testing focuses on variations and edge cases without relying on the backend.

We are using storybook to build our component and to render our component in isolation. We build stories for each component and on top of these stories we are running our tests because playwright does not really care, as long as you give it a web page, it does not really care what's inside the web page. You just need to provide the URL and playwright will open it. So we are spawning our storybook and then on top of it we run the test and we use playwright also for the browser automation.

Playwright started as a browser automation only but it also has now a great test runner that has a lot of really advanced functionality and a great setup, and this is how we actually test our components. This is what it looks like. I'm not sure how useful that is, but you can see here I have it in VS Code and I'm just running the test, it opens the page, it shows me the specific component and I can simply go and see it. Let's see that quickly again. You're running the test inside VS Code. You can see quickly that it passes and the test passes.

So what do we gain from this approach? First of all, this is real component rendering, we don't need to mock anything, the browser is responsible for that. We already write stories for storybooks to show our component so we reuse them and we run the test on top of them. This is a very stable test, we don't need to mock, Playwright is providing a very good feedback Playwright is providing a lot of stability in our tests, we get efficient execution, it can run in parallel, it can run in headless, which means it can run on the CI, it can also shards or you can run it across multiple machines in the CI, you basically have a lot of access to everything, just like any node or program that you want to run. Playwright, and I mean this could be a whole talk about how advanced debugging and tracing can do that. And we get the ability because this is running in the real browser, we can do screenshots and we can do visual regression tests. We can do that in Storybook, we can do that in Playwright and so on. I just want to mention a few other things that they are also doing, Storybook is also advancing their testing capabilities, Playwright has component testing so you can use it not just only for end-to-end and same goes for Cypress, they provide component testing. To make this quick, we can see here that when we talk about end-to-end tests and component testing, we have a real browser and visual migration can achieve this both, but the main difference is this, that in end-to-end we will probably work with a real backend server and make requests to real server and database and we will test complete user flows from ordering to payment, but because they are so long we will probably focus on the happy path while in component testing we will lock our backend, we don't want to go to our backend to run it and there are a lot of tools for doing that and we will focus more on variation and end case.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

TestJS Summit 2021TestJS Summit 2021
31 min
Test Effective Development
Top Content
Developers want to sleep tight knowing they didn't break production. Companies want to be efficient in order to meet their customer needs faster and to gain competitive advantage sooner. We ALL want to be cost effective... or shall I say... TEST EFFECTIVE!But how do we do that?Are the "unit" and "integration" terminology serves us right?Or is it time for a change? When should we use either strategy to maximize our "test effectiveness"?In this talk I'll show you a brand new way to think about cost effective testing with new strategies and new testing terms!It’s time to go DEEPER!
TestJS Summit 2023TestJS Summit 2023
32 min
The Art of ‘Humble Views’: Testing React Native Apps the Smart Way
In this talk, we explore the divisive world of testing, where developers often find themselves torn between writing no tests and striving for 100% test coverage. Learn how to navigate these polarizing positions and adopt a more nuanced strategy that makes testing efficient and effective.We'll dive into the concept of 'Humble Views,' where we minimize untestable objects by extracting logic from UI elements into test-friendly parts of the codebase. This approach simplifies testing, focusing on business logic instead of UI complexities. Discover how the Model-View-Presenter (MVP) architecture helps achieve this, with presenters serving as a logical layer for testing and hooks aiding in separating logic from UI components.Throughout the talk, we'll discuss the trade-offs of this approach, the role of End-to-End (E2E) tests, and how to strike the perfect balance between too much and too little testing. Join us as we delve into the art of creating 'Humble Views,' ensuring that our React Native apps are scalable, maintainable, and effectively tested!
TestJS Summit 2021TestJS Summit 2021
20 min
It's a (Testing) Trap! - Common Testing Pitfalls and How to Solve Them
It’s a trap” - a call or feeling we all might be familiar with, not only when it comes to Star Wars. It’s signalizing a sudden moment of noticing imminent danger. This situation is an excellent allegory for an unpleasant realization in testing. Imagine having the best intentions when it comes to testing but still ending up with tests failing to deliver you any value at all? Tests who are feeling like a pain to deal with?
When writing frontend tests, there are lots of pitfalls on the way. In sum, they can lead to lousy maintainability, slow execution time, and - in the worst-case - tests you cannot trust. But it doesn’t have to be that way. In this session, I will talk about developers’ common mistakes (including mine), at least from my experience. And, of course, on how to avoid them. Testing doesn’t need to be painful, after all.
React Summit 2022React Summit 2022
32 min
Design-Driven Full-stack: an End-to-End Dev Workflow that Scales
I’m going to show you something you haven’t seen before — a simple, integrated workflow made possible by React, RedwoodJS, and Storybook. We’ll start from scratch, generate code for components, design and mock state, then finish with a secure API and CRUD UI.
Sounds hard? It was. But not anymore! 🤯
You’ll learn how to bridge existing development gaps between stateful designs, data fetching, and your API using Redwood Cell components — a one-stop-React-shop for fetch, state, mocks, and design. Teams can move faster. Solo devs can iterate more quickly. And there are secondary benefits from documentation to security to accessibility, which add up to improving long-term maintainability at scale.
Get ready to be inspired by what you’ll be able to build!
TestJS Summit 2021TestJS Summit 2021
33 min
Test your UI in the REAL Browser
Imagine writing a complex function without unit tests. You would have to verify every scenario manually, over and over again. Cumbersome, but that's how most teams build user interfaces.
Imagine if you could build UIs and test UIs in the same place. If your components included expectations for how they were supposed to behave, you'd know the instant they broke.Storybook provides an organized approach to building UIs. You document a component's use-cases as stories, which are then rendered in isolation. Stories are like tests, but for UI. Storybook interaction testing allows you to script interactions and check expectations in the story itself. That allows you to run and debug UI tests in the same environment UI components are developed for: your browser.

Workshops on related topic

JSNation 2022JSNation 2022
148 min
Should we have business logic in the UI?
WorkshopFree
How many times did you say or hear “this is business logic, it should not be here”?In this workshop, we will create a modern frontend application using old patterns and you will learn how to build apps that have decoupled UI and services.We will start with a React application that has its whole logic in the UI. Then step by step we will extract the rules and operations to hit that sweet spot of independence.