Create a new synthetic test

edit

This functionality is experimental and may be changed or removed completely in a future release. Elastic will take a best effort approach to fix any issues, but experimental features are not subject to the support SLA of official GA features.

Have a question? Want to leave feedback? Visit the Synthetics discussion forum.

Syntax

edit

To write synthetic tests for your application, you’ll need to know basic JavaScript and Playwright syntax. Synthetics agent exposes a an API for creating and running tests:

  • journey(name, ({ page, browser, client, params }) => {}) — A journey tests one discrete unit of functionality. For example, logging into a website, adding something to a cart, or joining a mailing list. Each journey provides a fresh playwright browser, context, and page instance. See Create a journey for more information.
  • step(name, function) — A journey consists of multiple steps, i.e., actions that should be completed in a specific order. Steps are displayed individually in the Uptime app for easy debugging and error tracking.
  • beforeAll(function) — Runs a provided function prior to all journey runs. If the provided function is a promise, the runner will wait for the promise to resolve before invoking the journey. This function is useful for setting up global state or a server that will be used across multiple journeys.
  • before(function) — Runs a provided function prior to a single journey runs. This function is useful for setting up global state or a server that will be used for a single journey.
  • afterAll(function) — Runs a provided function after all the journeys have completed. This function is useful for removing global state or closing a server that was used in multiple journeys.
  • after(function) — Runs a provided function after a single journey has completed. This function is useful for removing global state or closing a server that was used in a single journey.

Playwright is browser testing library developed by Microsoft. It is reliable and fast and features a modern API that auto waits for page elements to be ready.

Create a journey
edit

The journey function takes a single parameter, name, and provides access to page, params, browser, and context:

  • browser — A browser is created by Playwright.
  • page — A page object from Playwright that lets you control the browser’s current page.
  • context — A browser context that doesn’t share cookies or cache with other browser contexts.
  • params — User-defined variables that allow you to invoke the Synthetics suite with custom parameters. For example, if you want to use a different homepage depending on the env (localhost for dev, and a URL for prod).

See the official Playwright API documentation for more information.

Putting it all together, a basic, two step journey might look something like this:

journey("Journey name", async ({page}) => {
    step("Step 1 name", async () => {
      // Do something here
    })
    step("Step 2 name", async () => {
      // Do something else here
    })
});
Create a step
edit

Steps can be as simple or complex as you need them to be. The Playwright page API reference will be your friend while writing tests.

A basic first step might simply load a page:

await page.goto('https://www.elastic.co'); 

See the page.goto reference for more information.

A more complex second step might wait for a page element to be selected, and then ensure that it matches an expected value. Consider the following HTML snippet:

<header class="header">
  <h1>todos</h1>
  <input class="new-todo"
    autofocus autocomplete="off"
    placeholder="What needs to be done?"
</header>

You can verify that new-todo class element contains the expected placeholder (input hint) with the following test:

step("Check placeholder text", async () => {
  const input = await page.$('input.new-todo'); 
  deepStrictEqual(await input.getAttribute('placeholder'), "What needs to be done?"); 
})

Query the page for the input.new-todo selector. See the page.$ reference for more information.

Return the placeholder attribute for new-todo with getAttribute(), and ensure it equals the provided text. deepStrictEqual is provided by Node.js and tests for deep equality between the actual and expected parameters. See the Node.js API reference for more information.

Sample Synthetic test
edit

A complete example of a basic synthetic test looks like this:

import { journey, step } from '@elastic/synthetics';
import { deepStrictEqual } from 'assert';

journey("Ensure placeholder is correct", async ({page}) => {
    step("Go to elastic.co", async () => {
      await page.goto('https://www.elastic.co');
    })
    step("Check placeholder text", async () => {
      const input = await page.$('input.new-todo');
      deepStrictEqual(await input.getAttribute('placeholder'), "What needs to be done?");
    })
});

Running synthetic tests

edit

As explained in the quickstart, there are two ways to run synthetic tests:

  • Run as an inline journey — copy/paste into heartbeat.yml
  • Run a test suite — import your tests into Heartbeat

Which option is right for you? That depends. If you want to invoke a single journey with few steps, an inline journey might be the easiest workflow — you only need paste your tests into your heartbeat.yml file. if you have a complex test suite, or your tests need to live with your application code, you’re likely better off setting up a synthetic test suite.

Run an inline journey
edit

The easiest way to run a synthetic test is by creating an inline journey. The journey keyword isn’t required, and access to variables like page and params is automatic.

Copy and paste your test steps into heartbeat.yml. Heartbeat spawns a separate Node.js process, schedules your tests, and runs them on a chromium browser. You don’t need to worry about anything else.

An example, short.js, is provided in the elastic/synthetics GitHub repository:

// test-homepage-hover.js
step("load homepage", async () => {
    await page.goto('https://www.elastic.co');
});
step("hover over products menu", async () => {
    await page.hover('css=[data-nav-item=products]');
});

To run this, or any other inline example locally, change into the directory of your test, and pipe the file contents to the npx @elastic/synthetics command.

For example:

cat examples/inline/short.js | npx @elastic/synthetics --inline

If everything works as expected, you’ll get the following response:

Journey: inline
   ✓  Step: 'load homepage' succeeded (1831 ms)
   ✓  Step: 'hover over products menu' succeeded (97 ms)

 2 passed (2511 ms)

The script can then be copied into your in your heartbeat.yml:

heartbeat.monitors:
- type: browser
  id: my-monitor
  name: My Monitor
  schedule: "@every 1m"
  source:
    inline:
      script: |-
        step("load homepage", async () => {
            await page.goto('https://www.elastic.co');
        });
        step("hover over products menu", async () => {
            await page.hover('css=[data-nav-item=products]');
        });

That’s it! You can either spin up Heartbeat yourself, or jump to Step 3: Run the container, connecting it to Elasticsearch of the Quickstart to use the provided Docker project template.

Run a test suite
edit

If you have a suite of tests you’d like to implement, you can use Elastic synthetics as a library. In this method, you use Docker to run both Heartbeat and elastic-synthetics.

Install the @elastic/synthetics package globally to get started:

npm install -g @elastic/synthetics

Now it’s time to write your tests:

  1. Create a new NPM/Node.js project.
  2. Create a javascript or typescript file that imports your tests. All synthetic test files must use the .journey.ts or .journey.js file extension.
  3. Compile everything together.

At Elastic, we’re fans of examples, so one is provided in the elastic/synthetics repository. If you’d like to test it locally, clone the repo, and install the example:

# Check out the synthetics repo and included examples
git clone [email protected]:elastic/synthetics.git &&\
cd synthetics/examples/todos/ &&\
# Install all required dependencies for the todos example
npm install

You are now inside the a synthetics test-suite, which is also an NPM project. You can now run the provided tests; all files matching the filename *.journey.* will be run.

Please note, you must run the following commands from within the examples/todos folder. The todos folder has its own package.json, which makes it its own NPM project. If you run these commands outside the todos folder you can run into confusing situations with NPM’s module resolution that can cause this command to not work.

# Run tests on the current directory. Please note the dot `.` which indicates
# that we want to run tests on the current directory
npx @elastic/synthetics .

Once you have your tests up and running, follow the steps in the quickstart guide to integrate with the provided Docker project template. You’ll need to write some additional orchestration to get Heartbeat on a box, pull your source of tests, and share it with Heartbeat.