Loading...

Simulating AWS IoT in the E2E tests

 The Internet of Things connects the physical world to the Internet so that you can use data from devices. In the world of testing of the IoT there are many aspects which has to be taken into account staring from testing physical devices to testing whole system. I will assume that my company is responsible only for web application which collects data from devices. This web application is for me the whole product so I have to simulate connected devices. In this article I will show you haw to do it when AWS is used for IoT application.

To make the goal more clear I want to have end to end tests in which I will be able to test three scenarios of application which manages home devices(like heater, light ect.) from the Web Page. Those scenarios will be containing steps performed on the web page with use of Selenium web driver but I will not be digging into details of those steps. The part I will focus on will simulating IoT devices which will be getting commands from application, reports to it or respond. To simulate those devices I will use simple library called AWS-IoT-Device-Mock.

In first scenario I will open the Web application, login as a user of the desired home, navigate to the devices from living room and open heater details. My assumption is (in real it has to be specified) that opening heater details send the request for actual temperature in information:

{
   "uuid":"5ccd6d12-7e40-4f51-b7fb-5a8bba266bf4",
   "get":{
      "option": "temperature"
   }
}

The message will be sent to MQTT topic: e/iot/pl/home0001. I expect that the device should respond on that message by sending the actual temperature in new message:

{
   "uuid":"5ccd6d12-7e40-4f51-b7fb-5a8bba266bf4",
   "response":{
      "option":"temperature",
      "value":21
   }
}

This very message will be send to MQTT topic: r/iot/pl/home0001. After publishing this message actual temperature value should be displayed on the web page with heater details. On the listing below we can see the test which verify this scenario.

public class SmartHomeHeaterTestTest {
    private IoTDeviceSimulator deviceSimulator;

    @Before
    public void setUp() {
        deviceSimulator = new IoTDeviceSimulator(
                AWS_SSM_REGION,
                IOT_CLIENT_ENDPOINT,
                ACCESS_KEY_SSM_PARAM,
                SECRET_KEY_SSM_PARAM);
    }

    @Test
    public void actualTemperatureShouldBeDisplayed() {
        final int expectedTemperature = 21;
        deviceSimulator
                .when()
                    .messageTopic("e/iot/pl/home0001")
                    .subscribeMessageBody("{'uuid':'{uuid}', 'get':{'option': 'temperature'}}")
                .then()
                    .publishTo("r/iot/pl/home0001")
                    .publishMessageBody("{'uuid':'{uuid}', 'response':{'option':'temperature', 'value':" + expectedTemperature + "}}");

        deviceSimulator.start();

        openSmartHomeWebPage()
                .loginAs(USER_OF_HOME_0001)
                .selectRoom(LIVING_ROOM)
                .openHeaterDetails()
                .assertThat(HeatersDetailsPageAssertion.class)
                    .displayedTemperatureIs(expectedTemperature)
                .assertEnd();

        deviceSimulator.stop();
    }
}

There are a few important parts in the code above. In the setUp method I am creating new IoTDeviceSimulator and as a parameters to constructor I have to pass the AWS region of IoT client endpoint, IoT client endpoint, name of the SSM parameters in AWS in which I keep the access key and secret key. To make it working access to the AWS on desired region has to be configured on the machine I want to run the code on. The next important part is defining the behavior of the device. After phase when() we defined what message we expect on subscribe topic. After phase then() we defined what message will be published to which topic when subscribed message is registered. You can see that in the code above value of the uuid in subscribeMessageBody and publishMessageBody is '{uuid}' which means that uuid value from real request will be extracted and passed to the publish message body in all places where value is '{uuid}'. Device simulator starts working after method deviceSimulator.start() so after that we can perform our steps in the web application. The application publish message which will be collected by IoTDeviceSimulator. IoTDeviceSimulator immediately resend the response on defined topic. So the only thing we have to take into account in the test is to wait for the update of the temperature on the Web Application side. After executing the steps we can shut down the IoTDeviceSimulator with command deviceSimulator.stop().

AWS-IoT-Device-Mock library gives also possibility to trigger publishing message to desired topic. To do that we only need the then() part to be defined of the IoTDeviceSimulator e.g.:

deviceSimulator
   .then()
      .publishTo("r/iot/pl/home0001")
      .publishMessageBody("{'uuid':'5ccd6d12-7e40-4f51-b7fb-5a8bba266bf4', 'info':{'option':'lock_status', 'value':true}}");

Now we can trigger publishing this message to defined topic with use of method deviceSimulator.publish(). Of course this method has to be invoked after starting device simulator.

The last functionality of AWS-IoT-Device-Mock I want to talk about is possibility to check whether expected message has reached the defined topic. To make it possible we need to define only when() part e.g.:

deviceSimulator
   .when()
      .messageTopic("e/iot/pl/home0001")
      .subscribeMessageBody("{'uuid':'6aad6d12-7e40-4f51-b7fb-5a8bba266bf4', 'get':{'option': 'reboot'}}");

To verify that the message is registered we have to trigger the method deviceSimulator.doesExpectedMessageReachedSubscribedTopic(). The method returns true when the message is registered but when it is invoked second time then it return false unless the message is registered again.

On the end I have to mention that even the library is very simple it can have some issues;)