JavaScript UI Testing: Best Practices

updated on 13 September 2022

The quality of the UI can make or break a web application. The first impression the user gets is crucial and you may never get a second chance. UI testing has become an increasingly important part of application development. UI testing is the process of verifying aspects of a program that a user will come into contact with. But what methods or techniques should you use to maximize the efficiency of your testing? In this article, you’ll learn about UI testing best practices that you can start using today. Let’s begin.

Automate Your UI Tests

By using a UI testing tool, you can:

  • Make sure that UI elements aren’t clashing or out of place
  • Check for incorrect spelling, capitalization, or punctuation
  • Ensure clarity and legibility of typography, including color and font size
  • Verify the alignment of images
  • Make sure that the browser displays error messages properly
  • Check the position of UI elements in different screen resolutions

But if these tests aren’t automatic, the process can be burdensome. Manual testing is powerful and might work if the software has a limited number of UI elements or is in the early stages of development. However, as the software picks up more features and more UI elements are added, manual verification becomes unmanageable.

With automatic UI testing, you can put the program through different test scenarios with different variables. Because tests are automated, you can run tests more frequently and detect errors as quickly as possible. Another benefit of automatic UI testing is lowering the cost of development. With automated tests, the need for manual testers is eliminated, which can save time and money.

Take Advantage of a BDD Framework

Behavior-Driven Development (BDD) is a methodology that helps programmers, testers, and managers fully understand each other in a software project. This methodology facilitates collaboration among team members so that everyone is on the same page on how the program should behave.

BDD is particularly beneficial when performing UI tests, but you can also use it for other types of testing, including unit and integration tests. Let’s say you want to write a short script to verify the functionality of a gaming app. Consider the following hypothetical example:

Feature: user buys a skin

  Scenario: Account has adequate coins
   Given the account has compatible character
 	And the account balance is at least 50 coins
 	And the account level is at least 5
	When the user confirms purchase
	Then the game should add the skin to the account
 	And the account balance should be reduced by 50 coins
 	And the skin should be automatically applied to the character

This example is written in BDD style using Gherkin syntax. If you’re a new employee, would you rather read dozens of lines of code or read this BDD scenario?

When you write your tests with BDD, you save other team members time. And should you come back to work on the tests yourself months later, you will find it easier to refresh your memory and so save your own time as well. BDD has the further advantage in that it lets non-developers, such as the project manager, understand tests and requirements.

Don’t Use sleep() To Pause the Test

The performance of your application hinges on internal and external factors such as server load, network bandwidth, and the processing power of the user’s device. Since you cannot always control these factors, you cannot predict how much time your app takes to load on the user’s device.

To address this problem, you may want to simulate possible delays by implementing a timeout function to halt the testing script for a specific time. But if you’re not careful, you may introduce instability to your UI tests.

Suppose you want to write a test script that opens your application’s home page and checks the visibility of the navigation menu. You’ll need to create two functions: one to open the app and the second to check the navigation elements.  

You also want to pause the execution of the test a little bit so that the app is fully loaded. If you’re using the Java language in Selenium, a browser-based automation suite, there’s a function called `sleep()` that allows you to do just that:

Thread.sleep();

The sleep() method takes a number in milliseconds and halts the thread's execution for the specified number of milliseconds (most other languages have a similar function). Say your app takes, on average, 4 seconds to start. And you want to halt the test for 8 seconds to be safe. So, you add Thread.sleep(8000); to the code.

Using the sleep() method in this way would be a great way to kill the efficiency of your test. Why? Because you’re wasting about 4 seconds of execution time for each test. While this isn’t much for one test, those seconds quickly add up if you have a large project with hundreds (if not thousands) of UI tests on multiple browsers.

Moreover, what if, because of some external factors, your app load time takes more than 8 seconds? Your tests will fail. A better approach is to use the implicitlyWait() function. 

By implicitly waiting, you order the browser to wait for a specific duration when trying to find an element. So, as soon as the element is found, the script is executed, and no time is wasted. And if the element isn’t available in the specified time, the test is reported as failed. Here’s an example:

WebDriver driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(8));
driver.get("http://exampledomain/index");
WebElement myDynamicElement = driver.findElement(By.id("nav"));

Implicit waiting can save both the time and cost of testing.

Set a Naming Convention for the Test Cases

Before creating tests, it’s essential to have a naming convention in place. Every test name should provide a clear and self-descriptive idea of what the test verifies. By creating and following a naming convention, your team members can:

  • Quickly search for a specific test
  • Immediately recognize what each test does even months after they wrote them
  • Identify which operation is causing a test to fail by just looking at the test name

A good naming convention should reduce the effort required to understand source code so you can focus on more critical issues. Here’s an example of a poorly named test:

@Test
public void homepageTest () {.....}

This name doesn’t tell you anything about the test other than it’s testing the homepage. Compare:

@Test
public void userShouldSeeHomepageTitleInBoldFont() {.....}

The name of this test clearly describes its purpose. Now, if the test fails, you immediately know which part of the app you need to fix.

Don’t Rely on One Type of Testing

You may come across developers that insist UI testing isn’t worth the trouble, and unit testing is all you need to verify the quality of the software. And then there are some developers who only do UI testing and disregard other tests. The reality is unit testing and UI testing are two approaches that complement each other. How?

In unit testing, you check each section of the source code (known as the "unit") separately to ensure it meets the needs of the application. This characteristic of unit tests allows them to be straightforward and fast.

But unit tests aren’t an accurate representation of the program in the real world. They only check parts of the source code in isolation and do not consider external dependencies. There are all sorts of flaws that unit tests can’t detect.

And then there’s integration testing, which enhances unit testing by combining units of the code and testing them as a group. This way, any defect that may arise when the units are used together is exposed. But again, integration testing doesn’t consider the user’s interaction with the UI.

That’s where UI testing comes in. UI testing lets you verify the application's visual elements, such as images and links, as well as user actions like mouse clicks and keyboard presses.

That doesn’t mean you should only stick with UI testing, though. Unit and integration tests are essential in the Quality Assurance (QA) approach, and you should use them along with UI tests.

Take Advantage of Headless Browser Testing

In the previous section, we discussed the importance of unit and integration testing and how they complement UI testing. But there’s another type of testing you may want to use in case you run into performance issues: headless browser testing.

Regular browsers have to render HTML, process CSS, and load media such as images and videos, which affect the speed of your UI testing script. Moreover, they consume a lot of memory when you execute tests in parallel.

The advantage of headless browser testing is that the browser won’t be loading the application's graphical user interface. As a result, tests are performed much faster and require less processing power.

Several browsers are designed especially for this purpose, such as PhantomJS and HtmlUnit. But some regular browsers like Google Chrome and Mozilla Firefox can also run in a headless environment.

Headless browser testing is particularly beneficial when testing interactions such as form submission and mouse-clicking. You may also use it to monitor the network traffic or handle JavaScript execution.

Don’t Run All Tests in All Target Browsers

Sometimes running your tests in multiple browsers does not improve the reliability of your application. The goal of testing your app in different browsers is to achieve better browser compatibility. But not all tests need to run in multiple browsers.  

For example, say you want to check the functionality of a form on the three most used browsers (Chrome, Safari, Firefox). You want to verify two things: 1) the form is displayed correctly in those browsers, and 2) the form responds appropriately when the user hits the submit button.

The first goal involves verifying that the inputs, checkboxes, and buttons are correctly displayed and work as they're supposed to. Should you run these tests on all three browsers? The answer is yes. But should you run each test 100 times to confirm that? No. Running these tests a few times would be more than enough to verify their functionality.

The second goal is different, however. Verifying the form response is not connected with the browser compatibility testing. Therefore, you can do it in just one browser. Running all tests in all target browsers would be misguided and may squander the cost savings you gain by incorporating test automation.

Use the Right UI Automation Testing Tools

So, you’re finally ready to begin using automated UI testing. But what testing tool best suits your application? There are various automated UI testing tools on the market, including Selenium, Meticulous, Applitools, Percy, Storybook, Chromatic, Cypress, Puppeteer, and Playwright.

The first step in selecting an automated testing tool is whether it supports your platforms and technology. Are you going to test a mobile application or a web application? Does it support your programming language, and on what operating systems?

Does the testing tool provide a self-healing algorithm? Self-healing allows the tool to detect changes in the user interface and adapt the testing to ensure the testing can go on without human involvement.

Does the testing tool support a BDD framework? Without BDD, some team members may not understand what’s happening in the testing project.

A good testing tool should be able to meet all your testing requirements.

Conclusion

When it comes to quality assurance, software testing should be at the top of your to-do list. In this article, we learned how automating your testing process will significantly increase the quality and frequency of tests.

We also looked at different types of testing. A robust testing strategy doesn’t involve only one type of testing, such as unit or UI testing. Instead, it incorporates different tests based on the application requirements.

Following a naming convention for test cases is often an overlooked yet essential part of testing. The name of each test should provide a clear idea of what it verifies.

Most often, it’s not necessary to run all possible tests in all target browsers. It would be a waste of time and resources. Instead, examine which tests you should test for cross-browser compatibility.

Don’t use sleep() to halt your test. Take advantage of the implicit wait so you can complete the test in less time.

Increase the efficiency of your tests by leveraging headless browser testing, especially when testing interactions such as form submission and mouse-clicking. Remember, a headless test skips loading the UI, so it’s multiple times faster.

Thanks for reading! Stay tuned to the Meticulous blog to learn more about app development best practices.

Meticulous

Meticulous is a tool for software engineers to easily create end-to-end tests. Use our open source CLI to open an instrumented browser which records your actions and translates them into a test. Meticulous makes it easy to integrate these tests into your CI.

Meticulous has an option to automatically mock out all network calls when simulating a recorded sequence of actions. If you use this option, you do not need a backend environment to use Meticulous and Meticulous tests never cause side effects (like affecting analytics) or hit your backend.

Try out the open-source CLI and create your first test in 60 seconds using our docs or watch the demo.

Authored by Faraz Kelhini

Read more