Setups

Setup classes describe your real-world test environment. They explain what you have (in contrast to Scenarios, which describe what you need). A Setup outlines the structure of Devices and their relationships to each other. It also includes the final implementation of all Features that the Devices should provide.

Add devices

You develop a Setup, by defining all Setup-Devices you have in your test rack or in your current test environment. If you want to test a real device, you normally has a computer, you device-under-test and so on. These are connected with each other. In the setup, you describe everything you have. This can also mean, that you have a client and a server, where the server may not be near you, but it is connected over the internet. You could also test a local installed program, so your setup could be this test-process-device and the program-process-device while both are connected over an inter-process-connection or an file. The term Setup is very broad and flexible.

You can start with the device that executes these tests. Often this is a normal computer or a server. This is your first device, that you can register to your Setup.

To develop a Setup, start by defining all the Setup-Devices available in your test rack or current test environment. For example, if you want to test a real device, you normally have a computer, your device-under-test, and so on. These components are connected to each other. In the setup, you describe everything you have. This could include a client and a server, where the server might not be physically near you but is connected over the internet. You could also test a locally installed program, so your setup might consist of a test-process device and a program-process device, with both connected via inter-process communication or a shared file. The term Setup is very broad and flexible.

Defining your first Device

Similar to Scenarios, you can create a new Device in your setup by simply defining a new inner class and inheriting from Device.

import balder

class SetupBasic(balder.Setup):

    class ThisDevice(balder.Device):
        ...
    ...

Add other device

Feel free to create additional devices as needed. You should include all the devices you intend to use in your tests within this setup. Balder will automatically identify matching variations between your setups and scenarios.

To expand our previous example, let’s add a new Device called DeviceUnderTest and connect it to the existing device using a Connection.

import balder

class SetupBasic(balder.Setup):

    class ThisDevice(balder.Device):
        ...

    @balder.connect(ThisDevice, over_connection=balder.Connection)
    class DeviceUnderTest(balder.Device):
        ...

    ...

That’s all for now. We have defined our devices and their relationships with each other. In the next step, we just need to add the supported Features for these devices.

Add setup-device features

Last but not least, we need to add some features to our devices. These features typically come from the definitions in the scenario. A scenario specifies the features it requires, so for a setup to match a scenario, the setup must provide implementations for those features.

If we have a scenario like the following:

# file `scenarios/scenario_simple_send_msg.py`

import balder
from lib.scenario_features.messaging_features import SendMessageFeature, RecvMessageFeature

class ScenarioSimpleSendMsg(balder.Scenario):

    class SendDevice(balder.Device):
        send = SendMessageFeature()

    @balder.connect(SendDevice, over_connection=balder.Connection)
    class RecvDevice(balder.Device):
        recv = RecvMessageFeature()

...

We need a setup, that has two similar devices, but with features that provides an implementation. This can be realized by overwriting the abstract features SendMessageFeature and RecvMessageFeature, like shown in the following snippet:

We need a setup that includes two similar devices, each equipped with features that provide concrete implementations. This can be achieved by overriding the abstract features SendMessageFeature and RecvMessageFeature, as shown in the following snippet:

# file: lib/setup_features/messaging_features.py

from lib.scenario_features.messaging_features import SendMessageFeature, RecvMessageFeature

class SendMessageFeatureImpl(SendMessageFeature):
    ... # provide an implementation for all abstract properties / methods

class RecvMessageImplFeature(RecvMessageFeature):
    ... # provide an implementation for all abstract properties / methods

We can now use our final implemented features within our setup:

import balder
import balder.connections as conn
# contains the implementation of the scenario features above (non abstract methods anymore
from lib.setup_features.messaging_features import SendMessageFeatureImpl, RecvMessageImplFeature

class SetupBasic(balder.Setup):

    class ThisDevice(balder.Device):
        send_impl = SendMessageFeatureImpl()

    @balder.connect(ThisDevice, over_connection=conn.HttpConnection)
    class DeviceUnderTest(balder.Device):
        recv_impl = RecvMessageImplFeature()

    ...

As soon as Balder is executed it will detect the variation between the SetupBasic and ScenarioSimpleSendMsg and runs the test ceases from ScenarioSimpleSendMsg. Internally it will replaces the feature attributes ScenarioSimpleSendMsg.SendDevice.send and ScenarioSimpleSendMsg.RecvDevice.recv with the setup instances of these final matching feature implementations SendMessageFeatureImpl and RecvMessageImplFeature.

Setup inheritance

Similar to scenarios, Balder supports inheritance for setup classes too, just like in standard Python class inheritance. This allows you to create a base setup with common elements—such as devices, features, connections, and test methods and then extend or modify it in child setup. It’s particularly helpful when you have similar test environments that share a lot of setup but differ in specific details. For example, you might have a general scenario for testing network communication, and then create child setups for specific protocols like HTTP or MQTT.

To use inheritance, simply subclass your new setup from an existing one. Balder will automatically inherit all the devices, features, connections, and test methods from the parent class. You can add new elements or override existing ones in the subclass.

Overwriting Setup-Devices

Similar to scenarios, you can overwrite devices and extend their feature set. Beside of providing the device parent class it is also important to use the same device names for the overwritten device like the name that is used in the parent class. This ensures that Balder can properly map, connect, and resolve the devices across the inheritance chain without conflicts.

Extending Features

You can freely add more features to the sub setup, by just adding them to the new device class. Similar to scenarios, it is also allowed to extend the functionality of existing features, by overwriting them. But be careful, you need to make sure to use the same attribute name and only assign features that are a sub class of the previous feature defined in the parent setup class.