Configuration

Directory Structure

An etr test module is similar to a wtools module structure but it has an etr.yaml file instead of wscript in the root:

<root>                  # module root
|-- etr.yaml            # main configuration file
`-- src                 # source directory for test files

A minimum example using Robot Framework:

<root>
|-- etr.yaml
`-- src
    `-- tests.robot

Contents of etr.yaml:

version: "1.0"

plugins:
    - etr.plugins.robot
robot:
    tests:
        - src/tests.robot

Main Configuration File

The etr.yaml configuration file has the following basic schema:

version: "1.0"

# Optional config, by default it's True
randomize: True|False

# List of plugins to load
plugins:
    - <plugin>

Then each loaded plugin might have their own schema with a root node. For example the robot plugin etr.plugins.robot has the following schema:

version: "1.0"

plugins:
    - etr.plugins.robot
robot:
    tests:
        - src/my-test.robot
        - src/my-other-test.robot

Note

By default test execution order, as specified in the configuration file, is randomized. For robot this means that one should write the robot test suite files without expectation that other test suites have been executed. This has the benefit of being able to execute a subset of tests without them breaking which is useful for speed. If this is infeasible the randomize: False can be set.

Plugins

Most of the heavy lifting in etr is done with plugins, where the core application just schedules execution of plugins. This chapter documents the built in plugins and how they are configured.

Note

The plugin execution order is determined by the plugins themselves using a fixed ordering value. The order of plugins in the configuration files does not make a difference.

etr.plugins.robot

This is the plugin that enables the running of Robot Framework tests. It executes the specified tests and creates a merged report with detailed test results.

The etr.yaml configuration is as follows:

version: "1.0"

plugins:
    - etr.plugins.robot

robot:
    mergedName: "top-level-suite"
    tests:
        - "src/my-test.robot"
        - "src/my-other-test.robot"

Where the list under tests robot/tests specifies the robot test files to be executed.

The attribute robot/mergedName determines the top-level suite name for the merged report. It is optional and by default the name of the directory containing the configuration file (etr.yaml) is used.

Note

etr also makes available keyword libraries with utilities that can be used in the robot tests. See Robot Framework Libraries for details.

etr.plugins.resources

To acquire test resources automatically from a set of available the plugin etr.plugins.resources can be used.

Test resources can be anything and etr does not use it directly. But attributes from acquired resources can be used to process configuration files using the etr.plugins.jinja2 plugin for example.

Resources can come from two sources:

  1. Local configuration files where configuration file must be provided with $RESOURCE_FILE or --resource-file option.

  2. Via the Resource Manager Web Service where the host must be provided with $RESOURCE_HOST or --resource-host option.

Compared to the Resource Manager Web Service the local resources it does not provide any schema validation. So a specific resource type is not guaranteed to follow a specific schema.

To use local resources, create a file with the following structure:

version: "1.0"

resources:
    <resource-id>:
        type: <type>
        tags:
            - <tag>
            - <tag>
        attributes:
            <attributes>

Where

<resource-id>

is the identifier for the resource in the file,

<type>

is the main resource type specifier.

<tag>

is an optional list of tags that can be used for filtering purposes by clients.

<attributes>

is the resource attributes that is added to the etr execution contexts so they can be used by e.g. other plugins.

An example configuration my-resources.yaml of two opcua-shutter’s with some attributes. Tags are optional and has been left empty in shutter2 in this example:

# my-resources.yaml
version: "1.0"

resources:
    "lab-shutter-1":
        type: "opcua-shutter"
        tags:
            - "v1"
        attributes:
            "server": "opc.tcp://134.171.12.186:4840"
            "namespace": 4
            "prefix": "MAIN.Shutter1"
    "lab-shutter-2":
        type: "opcua-shutter"
        attributes:
            "server": "opc.tcp://134.171.12.186:4840"
            "namespace": 4
            "prefix": "MAIN.Shutter2"

Note

The resource ids “lab-shutter-1” and “lab-shutter-2” are the identifiers for the resource and can be anything in the character class [A-Za-z0-9_-]. The resource IDs are only used for the purpose of identifying locked resources and is not directly seen by the user as the user defines their own name for the resource.

To acquire and use a resource the etr.yaml is updated to include request the resource “shutter” with type “opcua-shutter”:

version: "1.0"

plugins:
    - etr.plugins.robot
    - etr.plugins.resources

# Robot plugin configuration
robot:
    tests:
        - src/test.robot

# Resources plugin configuration
resources:
    "shutter":
        type: "opcua-shutter"

Note

The resource name “shutter” is the user provided name for the resource once it is acquired. This can be thought of as handle to the acquired resource attributes in e.g. the etr.plugins.jinja2 plugin.

etr.plugins.jinja2

The etr.plugins.jinja2 plugin allows the processing of template files using the Jinja2 template engine (c.f. the Jinja 2 documentation).

The etr.yaml configuration is simple and takes a dictionary or an array of dictionaries of files to process as "input" -> "output" files. An example that uses robot tests and Jinja2 follows:

New in version 3.0: Ability to order templates using array of objects.

version: "1.0"

plugins:
    - etr.plugins.jinja2
    - etr.plugins.robot
    - etr.plugins.resources

# Resources plugin configuration which acquires the
# required resources
resources:
    # My name/id for the acquired resource will be `shutter`.
    "shutter":
        type: "opcua-shutter"

# Jinja2 generates the src/test.robot file from the
# src/test.robot.j2 template file:
jinja2:
    files:
        "src/test.robot.j2": "src/test.robot"

# Robot finally runs the tests
robot:
    tests:
        - "src/test.robot"

To have a well-defined rendering order (first to last) the files can be specified as an array:

# ...

# Resources plugin configuration which acquires the
# required resources
resources:
    # My name/id for the acquired resource will be `shutter`.
    "shutter":
        type: "opcua-shutter"

# Order is important so an array of files is used
jinja2:
    files:
        - "src/first.j2": "src/first"
        - "src/second.j2": "src/second"

To acquire resources and run test using the resource file my-resources.yaml above etr is invoked as:

etr --resource-file=my-resources.yaml

To make use of the attributes of the acquired resource we use the resource name shutter as key in the dictionary resources which is made available in the Jinja2 environment.

Let’s imagine the acquired resource is the matching resource lab-shutter-2. The specification which includes the attributes we want to use from my-resources.yaml is again (using example from earlier):

# my-resources.yaml
# ... rest of file is omitted ...

"lab-shutter-2":
    type: "opcua-shutter"
    attributes:
        "server": "opc.tcp://134.171.12.186:4840"
        "namespace": 4
        "prefix": "MAIN.Shutter2"

Then to access e.g. the server attribute we use {{ resources.shutter.server }} for namespace we use {{ resources.shutter.namespace }}, and so on in the Jinja2 template.

Note

The name provided for a resource in the etr.yaml configuration is the local name for the resource which is used as the primary key to look up that resource in the “resources” dictionary provided in the Jinja2 template.

The file src/test.robot.j2 might look like this:

*** Settings ***
Library         Process
Library         Shutter.py

*** Variable ***
${PLC_SERVER}      {{ resources.shutter.server }}
${NAME_SPACE}      {{ resources.shutter.namespace }}
${PREFIX_DEVICE1}  {{ resources.shutter.prefix }}
${CMD_PORT}        5582

*** Test Cases ***
Reset PLC Controller
      Reset Controller  ${PLC_SERVER}    ${NAME_SPACE}    ${PREFIX_DEVICE1}
      Sleep             2

When this is rendered by Jinja2 to the specified output file src/test.robot, it contains all the expanded variables from the acquired resource (notice how Jinja2 variables are expanded):

*** Settings ***
Library         Process
Library         Shutter.py

*** Variable ***
${PLC_SERVER}      opc.tcp://134.171.12.186:4840
${NAME_SPACE}      4
${PREFIX_DEVICE1}  MAIN.Shutter1
${CMD_PORT}        5582

*** Test Cases ***
Reset PLC Controller
      Reset Controller  ${PLC_SERVER}    ${NAME_SPACE}    ${PREFIX_DEVICE1}
      Sleep             2

New in version 3.0: The ability to set and share variables across templates.

Using the do expr syntax in Jinja it is possible to set global variables that is accessible in the same and any other template that is rendered after the variable has been set (to make sure the templates are rendered in order you must use ordered files as described earlier):

{% do globals.update(variable="value") %}
{{ globals.variable }}

Where globals is the global dictionary variable provided by etr.

etr.plugins.nomad

The etr.plugins.nomad plugin allows deployment using Nomad job specification files (c.f. the Nomad documentation).

  • During setup each job specification file in nomad.jobs list is:

    1. Registered (deployed) with Nomad (if the job already exists the registration will fail).

    2. etr will monitor the job and wait for it to become stable (all tasks are running).

  • During teardown:

    1. Each job will be unregistered (undeployed) in reverse order.

The etr.yaml configuration is simple and takes dictionary of job specification files register (deploy). An example that uses nomad follows:

version: "1.0"
plugins:
    - etr.plugins.nomad

nomad:
    jobs:
        - "src/services.nomad"

Note

  • Currently support is limited to service job type.

  • Service discovery after deployment can be performed by using e.g. Consul.