Testing apps is critical to ensure quality and there are different types of tests, such as unit tests, integrations tests, and end-to-end tests. In this post, we’ll focus on automated end-to-end (E2E) tests and present the UI Test Automation Model (UTAM). UTAM is an open source solution built by Salesforce that lets you run E2E tests on any kind of app. We’ll walk you through a practical example with E-Bikes, one of our sample apps.

A brief introduction to end-to-end tests

E2E tests are often performed with an automated UI testing tool, such as WebdriverIO or Selenium. Unlike unit tests, which cover individual components in isolation, E2E tests closely mimic the interactions of business users. E2E tests use a real browser to interact with components and navigate across screens when executing test scenarios.

A key advantage of E2E tests is that they let you test an app even if you do not have full access to its source code. Let’s imagine that you want to test a Salesforce app that integrates with an AppExchange package. You do not have access to the sources of the Lightning Experience or the package. Yet, you can still test the app with E2E tests.

However, this power comes at a cost. An important drawback of E2E tests is that they are known to be fragile and hard to maintain. Whenever a web app changes, so does the DOM. These changes tend to break E2E tests because they are closely coupled with the DOM. In the context of our previous example, an update to the Lightning Experience, such as a new Salesforce release or a new package version, might break your tests.

This maintenance cost is the reason why it’s generally preferable to invest in unit tests as a priority, then integration tests, and finally, E2E tests. There’s a golden software development rule that summarizes it well:

The closer you are to source code, the cheaper it is to find and fix bugs.

With these pros and cons in mind, a Salesforce team came up with a game-changing proposition: the UI Test Automation Model (UTAM).

About UTAM

UTAM addresses the limitations of conventional E2E tests by making them easier to write and maintain. UTAM does not replace a UI testing tool, but it supplements it by decoupling the test code from the DOM thanks to page objects.

You write UTAM page objects in JSON with powerful and easy-to-understand grammar. Page objects are reusable blocks that let you reference the DOM of the page under test with CSS selectors. You can compose page objects to create advanced patterns.

The fact that page objects act as a shareable contract, and they are written in JSON, are major differentiators compared to other UI testing tools. This allows UTAM to work with a mostly language-agnostic approach. In other words, multiple teams can collaborate and share page objects regardless of the technology they use for testing.

When you are ready to run E2E tests, UTAM compiles your page objects into JavaScript, TypeScript, or Java depending on your project settings. Your tests can then leverage the compiled page objects to interact with your page.

Running end-to-end tests with UTAM

Now that we’ve covered what UTAM is and does, let’s look at some practical examples. We’ll start with a small “Hello, world” and show you how to test a real app: the E-Bikes sample app.

Testing a “Hello, world” example

Let’s start with something simple: a “Hello, world” example taken from the great UTAM tutorials. In this example, we’re testing a static HTML page that displays “Hello, 🌍 🌏 🌎!”


Here are the key steps needed to test this page:

  1. We declare a JSON page object that contains a world element. This element has a .world CSS class selector, which matches a DOM element on the page under test.
  2. We compile our page object, which generates a class with a getWorld method that is tied to the world element that we’ve defined in JSON.
  3. We use the getWorld method from the compiled page object in our test code to access the content of the DOM with the .world CSS class.

While this may seem verbose for a such a simple use case, the power of UTAM becomes obvious when the pages are more complex. In those cases (which are closer to real life apps), composition and reusability are a must have, and UTAM is here to help.

Testing the E-Bikes sample app

UTAM is not limited to testing Salesforce apps; it works with any technology or platform. But Salesforce provides a set of hundreds of reusable UTAM page objects for the base Lightning components (lightning-input, lightning-button, etc.) and the Lightning Experience (see links in resources section). This is a great time saver when writing E2E tests for Salesforce apps. This is why a number of Salesforce engineering teams use UTAM internally to test their apps.

Let’s look at how we run end-to-end tests on the Product Explorer Lightning app page of the E-Bikes sample app.


This page contains a number of standard and custom Lightning Web Components. Accessing the elements of those components by exploring the DOM of the page would be a real challenge without UTAM base page objects because we would have to traverse a number of Shadow DOM layers.

Here’s a diagram that helps to put this into perspective. It presents a simplified view of the nested components that compose this page. Custom components are represented in orange and Lightning base components in blue.

The custom components are unit tested, but our goal is to also run E2E tests to verify user interactions. This is the scenario that we are testing with UTAM:

  1. Check the default pagination information in the product tile list
  2. Check that the selected product card is empty by default
  3. Enter a search term in product filters
  4. Check the updated pagination information
  5. Select the first product tile
  6. Check that the selection is displayed in the product card
  7. Click on the product card’s button icon to open the product record page
  8. Ensure that the product record page is open

Before writing the code for the E2E test, the bulk of the effort goes into the definition of the UTAM JSON page objects for the custom components that are part of the test. For the sake of brevity, we won’t cover all of their definitions here, but you can have a look at the E-Bikes GitHub repository. In the folder that holds the metadata of the Lightning Web Components, you’ll find __utam__ subfolders with the JSON page objects.

For example, this is what the product filter page object looks like and how we retrieve the search input thanks to a Lightning input base page object and a .search CSS class selector:

{
    "shadow": {
        "elements": [
            {
                "name": "searchInput",
                "selector": {
                    "css": ".search"
                },
                "type": "utam-lightning/pageObjects/input",
                "public": true
            }
        ]
    }
}

Thanks to the UTAM page objects, the above E2E test scenario boils down to less than 100 lines of JavaScript, including test setup (not displayed here):

it('displays, filters and selects product from list', async () => {
    // Check default pagination info in product tile list
    let pageInfo = await productTileList.getPaginationInfo();
    expect(pageInfo).toBe(PAGINATION_ALL_ITEMS);

    // Check default empty selection in product card
    const productCardBodyText = await productCard.getBodyText();
    expect(productCardBodyText).toBe(SELECTION_EMPTY);

    // Enter search term
    const searchInput = await productFilter.getSearchInput();
    await searchInput.setText('fuse');

    // Wait for updated pagination info
    await productTileList.waitForPaginationUpdate(PAGINATION_FILTERED_ITEMS);

    // Check updated pagination info
    pageInfo = await productTileList.getPaginationInfo();
    expect(pageInfo).toBe(PAGINATION_FILTERED_ITEMS);

    // Select first product tile
    const productTiles = await productTileList.getTiles();
    await productTiles[0].click();

    // Wait for product selection
    await productCard.waitForSelectionUpdate('FUSE X1');

    // Open selected product record
    await productCard.clickOpenRecord();

    // Ensure that product record page is open
    await domDocument.waitFor(async () =>
        RECORD_PAGE_URL.test(await domDocument.getUrl())
    );
});

Thanks to UTAM, this test code is quite easy to read (it’s not cluttered with boilerplate code or CSS references), and it will likely be stable because it won’t need to updated when the DOM changes (when a CSS class is removed or renamed, for example). Most of the maintenance effort will be focused on the JSON page objects.

Closing words

We just scratched the surface of what UTAM can do. We saw a refresher on what E2E tests are, what UTAM is, and how it brings added value by decoupling test code from the DOM. Finally, we explored a couple of examples of E2E tests with UTAM. Head over to the UTAM website and take a look at the guide and the great tutorials to learn more advanced concepts, such as dynamic selectors or composition with methods, just to name a few.

Resources

About the author

Philippe Ozil is a Principal Developer Advocate at Salesforce where he focuses on the Salesforce Platform. He writes technical content and speaks frequently at conferences. He is a full stack developer and enjoys working on DevOps, robotics, and VR projects. Follow him on Twitter @PhilippeOzil or check his GitHub projects @pozil.

Get the latest Salesforce Developer blog posts and podcast episodes via Slack or RSS.

Add to Slack Subscribe to RSS