Examples

The examples are split into three directories:

  • charts

    • Contains the helm charts to deploy all the sample devices

  • single_device_proxy

    • The Tango device under test (Consumer) has a single tango.DeviceProxy to another Tango device (Provider)

    • Includes devices and sample tests

  • multi_device_proxy

    • The Tango device under test (Consumer) has multiple tango.DeviceProxy to Tango devices (Providers)

    • Includes devices and sample tests

Pact testing Tango devices (Consumer side)

A simple test sample:

from tango.test_context import MultiDeviceTestContext, DeviceTestContext
from tango import DeviceAttribute


from consumer_device import ConsumerDevice
from ska_pact_tango import Consumer, Provider, Interaction

devices_info = [
    {"class": ConsumerDevice, "devices": ({"name": "test/consumer/2", "properties": {}},)}
]


def test_consumer_to_provider_attribute_read():
    """Test the attribute read"""
    device_attribute_response = DeviceAttribute()
    device_attribute_response.value = 99.99

    pact = Consumer("test/consumer/1", consumer_cls=ConsumerDevice).has_pact_with(
        providers=[
            Provider("test/provider/1").add_interaction(
                Interaction()
                .given("The provider is in Init State", "Init")
                .upon_receiving("A read attribute request for the attribute random_float")
                .with_request("read_attribute", "random_float")
                .will_respond_with(DeviceAttribute, device_attribute_response)
            )
        ]
    )

    with pact:
        with MultiDeviceTestContext(devices_info, process=True) as context:
            consumer = context.get_device("test/consumer/2")
            assert consumer.read_provider_random_float() == device_attribute_response.value

For more examples refer to ska-pact-tango/examples.

Tango to with_request mapping

Tango syntax

with_request

provider_device.random_float = 5.0

with_request(“attribute”, “random_float”, 5.0)

provider_device.write_attribute(“random_float”, 5.0)

with_request(“method”, “write_attribute”, “random_float”, 5.0)

with_request(“write_attribute”, “random_float”, 5.0)

provider_device.random_float

with_request(“attribute”, “random_float”)

provider_device.read_attribute(“random_float”)

with_request(“read_attribute”, “random_float”)

provider_device.SomeCommand()

with_request(“command”, “SomeCommand”)

provider_device.SomeCommand(1.0)

with_request(“command”, “SomeCommand”, 1.0)

provider_device.command_inout(“SomeCommand”)

with_request(“method”, “command_inout”, “SomeCommand”)

with_request(“command_inout”, “SomeCommand”)

provider_device.command_inout(“SomeCommand”, 1.0)

with_request(“method”, “command_inout”, “SomeCommand”, 1.0)

with_request(“command_inout”, “SomeCommand”, 1.0)

Verifying the Pact contract with the provider

Every Pact contract should be verified against a Tango device (Provider) to ensure it’s valid.

There’s a helper decorator (verifier) that exposes the interaction to verify as well as the device name. The verifier decorator takes to location of the pact file and the interaction description as parameters. The description is used to get the interaction that you want to verify as there could be several in the Pact file.

Use the command line tool

Once the package is installed there should be a script named pact-tango-verify on the PATH. This can be used to verify the Pact contract file.

root@ska-pact-tango# pact-tango-verify  -h
usage: pact-tango-verify [-h] [-v] pact_file_path

Verify a Pact file against the provider

positional arguments:
  pact_file_path

optional arguments:
  -h, --help      show this help message and exit
  -v, --verbose   Print test output

Example

root@ska-pact-tango# pact-tango-verify ./examples/multi_device_proxy/verifier_test.json  -v
Loading Pact from ./examples/multi_device_proxy/verifier_test.json
Done
Checking against provider [test/multiprovider/1]
Checking interaction [A request to run add_int_to_five]

Write custom tests

Put the Provider in a desired state

You can add commands (and parameters) to execute as part of given and and_given in the pact definition. During the setup_provider step in your test they will be executed in order against the Provider. This will update the Provider so that it’s in the correct state prior to running verify_interaction.

See comments in the code sample below.

Example

import pytest

import tango

from ska_pact_tango.verifier import verifier
from ska_pact_tango.provider import Provider, Interaction
from ska_pact_tango.consumer import Consumer


pact = Consumer("test/consumer/1", consumer_cls=ProviderDevice).has_pact_with(
        providers=[
            Provider("test/nodb/providerdevice").add_interaction(
                Interaction()
                .given("The provider is in Init State", "Init")
                .and_given("with_arg_command has ran", "with_arg_command", 5)
                .and_given("no_arg_command has ran", "no_arg_command")
                .and_given("Only description")
                .upon_receiving("A request to run add_int_to_five")
                .with_request("command", "add_int_to_five", 5)
                .will_respond_with(int, 10)
            )
        ]
    )

@verifier("/path/to/pact_file.json", "A request to run add_int_to_five")
def verify_provider(*args, **kwargs):
    interaction = kwargs["interaction"]
    device_name = kwargs["device_name"]

    proxy = tango.DeviceProxy(device_name)

    # Here you can run commands on proxy that was not defined in the `given`s
    proxy.Standby()

    # setup_provider runs through the provider_states and executes the commands
    # In our case it will be:
    # - proxy.Init()
    # - proxy.with_arg_command(5)
    # - proxy.no_arg_command()
    interaction.setup_provider(proxy)

    # verify_interaction will execute the `with_request` and compare it to `will_respond_with`
    interaction.verify_interaction(proxy)

Device test playground

For setting up an environment where you can run tests and experiment with them, follow the steps below:

Setup

Introduction

Deploy the sample devices by means of a local helm chart in a Kubernetes namespace.

A development container where tests can be edited and executed is also included.

Each container mounts the ska-pact-tango directory in /app/

Install

  1. Ensure you are in the ska-pact-tango directory.

  2. Ensure that helm and kubectl has been set up.

  3. Deploy the chart of sample devices into a namespace (using pact below).

3.1. If you want to use the Jupyter Notebook, set your Ingress hostname

kubectl create namespace pact
helm install test ./examples/charts/pact-example   -n pact --dependency-update  --set pact_example.hostpath=$(pwd) --set ingress.hostname=<Your hostname>

3.2. If you don’t have Ingress configured you can omit the hostname. You will not be able to access Jupyter.

kubectl create namespace pact
helm install test ./examples/charts/pact-example   -n pact --dependency-update  --set pact_example.hostpath=$(pwd)

Open a shell to run commands in the container

kubectl exec --stdin --tty $(kubectl get pods -o name  | grep pact-example) -c dev-test -n pact -- /bin/bash

Run the sample tests:

python3 -m pytest /app/ska-pact-tango/examples/sample_tests.py

Access a Jupyter Notebook to run arbitrary code

Navigate to:

http://<your-hostname>/jupyter/

Password: pact

Clean up

helm delete test  -n pact
kubectl delete namespaces pact