testing & QA

Testing Android Things - IoT meets Java

Likes to Android with the aim of making that million dollar app one day; retiring to a beach of his choice. For now writes blog posts to hopefully enlighten others & gain some feeling of self worth.

Google has just announced the amazing Android Things. This gives the Internet of Things (IoT) a real jump start with the availability of the Android ecosystem and all the open source code and ready to use libraries that go with it. You'll want to start creating an IoT Android Things app as soon as possible but don't forget IoT devices need testing too. This blog explores ports and adapters and vendor tests to keep your codebase clean and your IoT devices solid.

Testing still needs to be done in the internet of things. However testing integration with hardware is awkward, and how can you unit test something that isn't technically a unit? You would need the peripherals plugged in to be able to take readings so you can test the output. But why are you testing the peripherals works as designed? They're built by the manufacturer, you shouldn't have to do their work for them.

Android Things provides Peripheral I/O APIs to communicate with sensors and actuators using industry standard protocols and interfaces.

Whilst Peripheral I/O APIs allow for standardised communication. They still allow all general behaviours that the manufacturer allows. What you want is a peripheral that does the specific things you expect. For example some manufacturers LED strip could show every colour in the rainbow, it can strobe, flash, fade and dim - but what you need is a consistent red or green. Let's try and test only what we need and what we can control.

When first using a new peripheral you should create the contract that you want the peripheral to adhere to. Using this contract (an interface in Java) you can create a mock of the peripheral that emits fake data, so that you don't have to have the physical peripheral plugged in. This contract can allow you to have unit tests for any classes that interact with the hardware peripherals.

If you have tests for this contract, you can plug in any alternative peripheral and test that it adheres to how you expect it to work. These are known as vendor tests, when changing from a Panasonic LED to a Huawei LED strip, your tests will stay the same and your confidence that it "just works" will be sky high.

There are two types of testing going on here. Unit testing that involves ignoring the hardware peripherals when testing your own code and Integration testing that involves determining the hardware works as expected in your system and can be changed out easily. Both of these different testing abstractions involve the use of a contract and the pattern known as ports and adapters.

Create your application to work without either a UI or a database so you can run automated regression-tests against the application, work when the database becomes unavailable, and link applications together without any user involvement.

- Ports and Adapters / Hexagonal Architecture

Ports & Adapters can allow your application to create a plug and play architecture. For each Port you declare the contract (in java an Interface) which you would like some other part of the system to adhere to. In the quoted example above this is a Port for reading a database. You then fulfil the contract by creating an Adapter (in java a class implementing the Interface) and in the example this would be a SQLite database or a PostgreSQL implementation. Ports & Adapters allow your application to be driven by users, other apps or automated tests; meaning you can develop and test in isolation away from the applications eventual run-time configuration.

Creating a Port for our LED peripheral we now have a contract we control. This contract can say:

At a glance it might seem that we are restricting the omnipotence & features of our LED and that doing so is bad but restriction is a good thing, let me explain. We've declared a contract that says "The LED in our system needs to turn off and turn on in red and green". Restricting the behaviour like this is what gives us a contract and is what allows us to control the LED as our own in our own system and be able to swap it out for any other manufacturers LED and still get the same functionality.

Now that we have a contract, we could implement it using a hardware manufacturers Peripherals SDK for their LED. Moreover we can also implement the contract with our own "mock" implementation. For example:

Port / Contract:

interface CoolLed {

    // ...


    void turnOnRed();


    void turnOnGreen();

}

Adapter / Panasonic implementation:

class PanasonicCoolLed implements CoolLed {

     // ...


    @Override
    public void turnOnRed() {
        ledInputs.enableLight();
        ledInputs.setColor(0xFF0000);
    }


    @Override
    public void turnOnGreen() {
        ledInputs.enableLight();
        ledInputs.setColor(0x00FF00);
    }
}

Adapter / Mock implementation:

class LoggingCoolLed implements CoolLed {

    // ...


    @Override
    public void turnOnRed() {
        Log.i("led", "ON - RED");
    }


    @Override
    public void turnOnGreen() {
        Log.i("led", "ON - GREEN");
    }
}

Having a mock implementation means we can carry on working on our system and use our Logging LED anywhere we want to integrate the real LED. The benefits of this are, you don't have to worry about having the peripherals to hand, plugged in or even working. When coding, instead of watching an LED turn on and off you can watch the console log our messages saying on and off.

I can't highlight enough how powerful this design pattern is. It allows you to concentrate on getting your system up and running without worrying about peripheral pieces. The separation of concerns means you can even have different people / teams working on the separate parts of the application as long as you agree on the contract. You can give demos even if the hardware cables were damaged in transit and you can fully unit test your system.

So far having this contract in place has allowed us to unit test our system because we can replace the hardware at testing time with our mock implementation, but what about the other side of the contract: should we test the implementation and how?

This blog post explains the theory of unit testing with Ports and Adapters. If you want to see a hands on example see my other blog post here.

Vendor Tests

Everyone says you shouldn't test other peoples code (as in external dependencies) but why not? It should have been tested by them so you are duplicating work? It is not under your control so how can you guarantee test consistency? You only use a subset of their library so how do you know when to stop testing? All of these are valid concerns for normal use of a library, however when integrating with a library you want to make sure the integration works, don't you?

Vendor Tests help you guarantee the sanity of the code you are integrating with. They are an integration test, so don't run as fast or as often as unit tests. You could write them once when you first meet a library then throw them away once you are more confident with how the library works. Or you can keep them around in your test suite and use them to guarantee your use of a third party library when you update it to the latest version.

The contract/port in our architecture allows for some powerful Vendor tests. We have the contract stating how our red or green LED peripheral should work, we now want to test that the Panasonic LED library can give us this functionality. Testing using the PanasonicCoolLed Adapter means we can show that the library is functioning as we expect and adhering to our contract. Note that we only test this library through the contract methods of the port, this approach ensures we are testing only the functionality we want and nothing more.

If our vendor tests assert the outcome of the methods on our contract (remember these are integration tests not unit tests) and we wanted to swap hardware from Panasonic to Huawei we would still be using our same contract/port. Therefore even if we change the Adapter, the same tests should still be able to run and still give us the same confidence that the new 3rd party library is working.1 Thinking of these tests in terms of testing the output of sensors might make more sense. For instance a vendor test suite around a temperature gauge to make sure you always receive the same degree of accuracy.

Conclusion

Android Things brings the Internet of Things to the Android Framework. Applying Java testing design principles to IoT takes just a little bit of new thinking. However once you do start architecting and testing for the Internet of Things you can see how powerful a good separation of concerns can be. Ports & Adapters lends itself to this separation and allows your system to become modularised around the core business logic that you want your device to apply and the separate peripherals that you want to use. This separation allows for testing at every layer, giving you more confidence in your own system and even better; a solid codebase that you can change the peripherals of whenever it is required or requested.

For a hands on walk through example of testing this way see my other blog post here.


  1. These being hardware peripherals the Integration test assertions for knowing if hardware is working might have to be a bit more clever. For instance, how do you assert that an LED has turned RED? You might start to think about creating custom test rigs with assertion sensors or leaving this for manual testing.

Enjoyed this article? There's more...

We send out a small, valuable newsletter with the best stories, app design & development resources every month.

No spam, no giving your data away, unsubscribe anytime.

About Novoda

We plan, design, and develop the world’s most desirable software products. Our team’s expertise helps brands like Sony, Motorola, Tesco, Channel4, BBC, and News Corp build fully customized Android devices or simply make their mobile experiences the best on the market. Since 2008, our full in-house teams work from London, Liverpool, Berlin, Barcelona, and NYC.

Let’s get in contact