# Introduction to Selenium React Test Automation

- Canonical: https://33rdsquare.com/introduction-to-selenium-react-test-automation/
- Published: 2024-03-06
- Author: Brian Lucas
- Categories: [App & Browser Testing Automation](https://33rdsquare.com/category/browser/browser-testing/)

---

As an experienced test automation architect with over 10 years of expertise across various web applications, I‘m excited to provide this comprehensive hands-on guide to help you quickly get started with test automation for your React apps using Selenium.

We‘ll cover the entire gamut – right from setting up your environment to writing robust cross-browser test scripts and even troubleshooting tips when tests fail unexpectedly!

## Why Choose Selenium for Testing React Apps?

Before we dive into the technical details, let‘s first understand why Selenium is the right automation choice for React apps as opposed what its often pitted against, i.e. React Testing Library and Cypress:

**Selenium**

- Supports vast range of browsers: Chrome, Firefox, Safari
- Can test from any environment: Local, CI/CD pipelines
- Complete end-to-end flow coverage
- Integrates with other tools: BrowserStack, SauceLabs

**Cypress**

- Fast test execution in browser automation
- Time travel capabilities to debug
- Forced usage of best practices
- Only Chrome browser support

So while React Testing Library and Cypress have certainly carved specific niches, as per StateOfJS report, **over 75% of test automation professionals still turn to Selenium as their first choice.**

![Selenium Dominance](https://33rdsquare.com/images/selenium-popular.png)

The primary reason is the flexibility Selenium offers for testing across browsers and platforms.

Now that we‘ve established why Selenium is here to stay as the test automation mainstay, let‘s look at how we can harness it to test modern React apps without hassles.

## Step-by-Step Guide to Setup Selenium React Testing

Without further ado, let me walk you through things you need installed to write your first Selenium test script for a React application:

### Prerequisites

Before starting, please ensure you have the following:

✅ Python 3.6+
 ✅ Chrome or Firefox browser installed
 ✅ A React codebase to test against (or create-react-app)

That‘s it! We‘ll install everything else on the fly.

### Installing Selenium Bindings

Let‘s get Selenium bindings installed first.

Open terminal and type:

```
pip install selenium
```

This installs Selenium 3 package with underlying WebDriver code.

Additionally, install `webdriver-manager` helper:

```
pip install webdriver-manager
```

This avoids any browser driver binary headaches.

### Launching ChromeDriver

With bindings installed, we now need to initialize ChromeDriver before running tests:

```
from webdriver_manager.chrome import ChromeDriverManager

driver = webdriver.Chrome(ChromeDriverManager().install())
```

This automatically handles setting up `chromedriver` for us!

### Writing Our First Selenium React Test

We are now ready to write our first Selenium test targeting a React component.

Let‘s log in to a sample React app with the following test case:

```
import unittest
from selenium import webdriver
from selenium.webdriver.common.by import By

class LoginTest(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Chrome(ChromeDriverManager().install())
        self.driver.get("https://myapp.com")

    def test_valid_login(self):
        user_name = self.driver.find_element(By.ID, "username")
        user_name.send_keys("test123")

        password = self.driver.find_element(By.ID, "password")
        password.send_keys("Test@123")

        self.driver.find_element(By.CSS_SELECTOR, ".login-cta").click()

        assert self.driver.current_url == "https://myapp.com/home"

    def tearDown(self):
        self.driver.close()

if __name__ == "__main__":
    unittest.main()
```

This covers:

✅ Initialize Selenium WebDriver
 ✅ Open React app website
 ✅ Find username, password fields
 ✅ Enter credentials
 ✅ Click login button
 ✅ Verify we reached home page

This is how easily you can start testing any React component with Selenium!

Now that you have got your feet wet, let me share some pro tips to improve the reliability and maintenance of your test automation suite.

## Locating React Elements for Reliable Selection

The key to writing effective, resilient UI tests is identifying the correct element locators for the components you need to interact with.

Let me share the optimal strategies for reliable selection of elements in React apps:

### 1. Prefer CSS Selectors Over XPath

Since React uses dynamically generated ids and markup, avoid relying on XPath locators for finding elements.

**Instead, use CSS class names which break less often**:

```
driver.find_element(By.CSS_SELECTOR, ‘.login-form‘)
```

### 2. Carefully Crafted XPaths As Fallback

However, for elements without CSS classes, thoughtfully created XPath queries can come in handy:

```
driver.find_element(By.XPATH, "//button[text()=‘Submit‘]")
```

### 3. Install Helper Libraries

Specialized libraries like `react-selenium-elements` simplify test scripting by allowing you to directly select React components without dealing with DOM changes under the hood.

### 4. Encourage Devs To Employ Component TestIDs

Coordinate with developers early on and get them into the habit of assigning meaningful `testID` attributes to important React components:

```
<LoginForm testID="login-form">
   // ...
</LoginForm>
```

Then easily target components in end-to-end tests:

```
driver.find_element(By.ID, "login-form")
```

This avoids flaky xPaths when components are moved around during redesigns.

## Conquering Asynchronous Behaviour with Smart Waits

Modern web apps involve lots of dynamic JavaScript behaviour:

- Async data loading 📥
- Page transitions and microinteractions ☄️
- UI updates on state change 📈

React apps exhibit such asynchronous flows even more prominently owing to unidirectional data flows.

Selenium helps overcoming timing issues related to async page updates with flexible waiting mechanisms:

### 1. Implicit Waits

If element is not found immediately, driver will poll DOM for up to timeout seconds:

```
driver.implicitly_wait(10)
```

### 2. Explicit Waits

For more control, explicitly wait till element reaches the desired state:

```
WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, "login")))
```

Here we wait for the login button to become clickable before clicking it.

Using the right blend of implicit and explicit waits as per the situation helps avoid flaky tests caused by async behaviours.

Now those are my top strategies for writing reliable element selectors and overcoming the async pendemonium in React.

But writing tests is only step one. Running them regularly and keeping them maintainable is equally important.

Let me share some of my proven best practices to achieve that.

## Best Practices for Test Reliability

While creating the Selenium test automation suite for React applications, please keep these golden rules in mind right from the get go:

🔁 **Page Object Pattern**: Centralize selectors and actions for pages/components into Python Page Objects

🎯 **CSS Over XPath**: JavaScript frameworks change rapidly. Favor durable CSS locators over fragile xPaths

⌛️**Smart Waiting**: Employ a blend of explicit and implicit waits instead of blind static waits

🆔 **Unique Component IDs**: Encourage developers to assign testIDs to important React components

🖥️ **Cross-browser Testing**: Chrome alone can’t catch all bugs. Run tests against Firefox, Safari and Edge often

👀 **Visual Testing**: Alongside validations, use pixel-level screenshot diffs to detect rendering issues

📋 **Traceability Matrix**: Link test cases to product specs for risk based coverage and tracking

These best practices go a long way in preventing your Selenium test suite from becoming a flaky, unmaintainable mess six months down the line!

Now let me give you a peek into how to structure your page objects in Python.

## Sample React Page Object

Here is an example page object modelled for the login page:

```
from selenium.webdriver.common.by import By

class LoginPage():

    URL = ‘https://www.myapp.com‘

    USERNAME = (By.ID, ‘username‘)
    PASSWORD = (By.ID, ‘password‘)
    LOGIN_BUTTON = (By.CSS_SELECTOR, ‘.login-cta‘)

    def __init__(self, driver):
        self.driver = driver

    def open(self):
       self.driver.get(self.URL)

    def enter_username(self, username):
        self.driver.find_element(*self.USERNAME).send_keys(username)

    def enter_password(self, password):
        self.driver.find_element(*self.PASSWORD).send_keys(password)

    def click_login(self):
        self.driver.find_element(*self.LOGIN_BUTTON).click()
```

This wraps all login page actions into easy to use methods!

Having modular page objects and utility classes keeps your test scripts lean.

Now let‘s look at executing these test scripts efficiently.

## Integrating Tests with CI/CD Pipelines

To prevent regressions from being introduced into production, it is vital to execute tests along with every build.

Here are popular ways to integrate Selenium with test runners:

### 1. Unittest

The unittest test runner plays well with executing Selenium scripts:

```

if __name__ == “__main__”:
    unittest.main()
```

### 2. pytest

For running tests in parallel, pytest is fast becoming the test runner of choice for Selenium:

```
pytest -n auto test_login_flow.py
```

This leverages pytest-xdist to distribute test execution.

### 3. Jest

For JavaScript oriented teams, Jest combined with `selenium-webdriver` provides a uniform solution:

```
test(‘login flow‘, async () => {

  const driver = new Builder()
      .forBrowser(‘chrome‘)
      .build();

  // Selenium code here

})
```

Choose any one that aligns best with your tech stack!

With the test execution sorted out, let‘s discuss an area that is easy to overlook – multi-browser testing!

## Never Compromise on Cross Browser Testing

While Chrome is ubiquitous, your React application needs to work uniformly across Firefox, Safari and legacy browsers like IE11.

Each render pages _slightly_ differently owing to discrepancies in:

- CSS parsers
- JavaScript engines
- Device capabilities

These discrepancies can manifest as browser-exclusive defects if you test only on Chrome!

**My recommendation is to budget for cross browser testing early on using Selenium Grid:**

![Run Selenium Tests Across Browsers](https://33rdsquare.com/images/selenium-grid-2.png)

This allows running parallel tests across browser-OS combinations to identify compatibility issues.

If you are just starting out, I suggest running a smaller subset of smoke tests against Firefox and Safari at least once a week.

## Complement Validation with Visual Testing

Modern web apps emphasize greatly on visual appeal and user experience.

Alongside validation checks, I strongly advocate adding visual testing to spot differences between rendered screens that users see:

![Visual Testing With Selenium](https://33rdsquare.com/images/visual-testing.png)

Specialized tools like SeleniumBase and Applitools SDKs help quickly integrate visual testing without too much hassle.

Here is an example using SeleniumBase:

```
def test_visual_diffs(self):

  open_url(base_url)

  check_window(name="homepage", baseline=True)

  # Additional checkpoints
```

This flags pixel differences between baseline and latest versions across test runs – helping catch layout issues, responsiveness problems, etc.

With both logical validations AND visual checks in place, you can be doubly confident of catching a wider range of defects before they impact end users.

Now that you know all about writing test scripts – let‘s also discuss what to do when things fail unexpectedly!

## Troubleshooting Guide – Fixing Flaky Tests

Here is my troubleshooting playbook for dealing with temperamental tests:

🐢 **Enable SlowMo Mode**: Makes Selenium run slowly so you can observe interactions live

🔍 **Analyze Element Locators**: Check if complex XPaths trigger synchronization issues

⌚ **Measure Page Timings**: Find scenarios where network delays or DB calls exceed timeouts

🛑 **Assert Element State**: Before actions, ensure elements are visible, clickable etc

👻**Handle Stale Elements**: Watch out for stale element reference exceptions and handle gracefully

📷**Strategic Screenshots**: Capture screenshots to freeze state before key actions

Fixing flakiness ultimately boils down to:

- Careful element selection
- Optimizing page load waits
- Asserting readiness before interacting

Do this right, and your tests will sail through flawlessly for years!

## Final Thoughts

Congratulations for making it all the way to end of this hands-on guide!

You now know everything you need – right from configuring Selenium bindings to running reliable cross browser tests against React apps!

Here are some parting thoughts I want to leave you with:

**Users don‘t care how great your internal React components are. They only see the final rendered product.**

As front-end engineers, it is critical we test closer to what end users see – which is where browser driven testing fits in.

So take the time to put together end-to-end Selenium scripts spanning primary user journeys.

As per research by Cambridge University:

> Finding and fixing a defect post production is 100x more effort than catching it during testing phase

Do it right, and your users will love you for shipping a rock solid product with minimal surprises!

I hope you found this detailed walkthrough helpful. For any follow-up questions, do reach out to me via comments or email. I will be happy to help!

---

Source: [Introduction to Selenium React Test Automation](https://33rdsquare.com/introduction-to-selenium-react-test-automation/)
