Unit Testing

New in version 3.0: (PlatformIO Plus)

Unit Testing (wiki) is a software testing method by which individual units of source code, sets of one or more MCU program modules together with associated control data, usage procedures, and operating procedures, are tested to determine whether they are fit for use. Unit testing finds problems early in the development cycle.

Test Types

Desktop

PlatformIO wraps test and main program (from src_dir) with own PlatformIO Unit Testing Framework, builds final program using Platform native and run test on a host machine (desktop).

Note

PlatformIO does not install any toolchains automatically for Platform native and requires GCC toolchain to be installed on your host machine. Please open Terminal and check that gcc command is installed.

Embedded

PlatformIO wraps test and main firmware (from src_dir) with own PlatformIO Unit Testing Framework, builds special firmware for a target device and upload it. After uploading, PlatformIO connects to embedded device (board) using test_port , starts test, collects results and shows test results on the host machine.

Currently, PlatformIO Unit Testing Engine supports these embedded frameworks:

Note

Please note that PlatformIO Unit Testing Framework uses Serial/UART as communication interface between PlatformIO Unit Test Engine and target device. If you use Serial in your project, please wrap/hide Serial-based blocks with #ifndef UNIT_TEST macro.

Test Runner

Test Runner allows to process specific environments or to ignore some tests using “Glob patterns”. Also, you will be able to ignore some tests for specific environment using test_ignore option from Project Configuration File platformio.ini.

Local

Allows to run tests on host machine or on the target devices (boards) that are directly connected to this machine. In this case, need to use platformio test command.

Remote

Allows to run tests on remote machine or remote target device (board) without any dependencies to OS software, extra software, SSH, VPN or opening network ports. Remote Unit Testing works in pair with PIO Remote™. In this case, need to use special command platformio remote test.

PlatformIO supports multiple Continuous Integration systems where you can run unit tests on each integration stage. See real PlatformIO Remote Unit Testing Example.

Design

PlatformIO Unit Testing Engine design is based on a few isolated components:

  1. Main Program. Contains the independent modules, procedures, functions or methods that will be the target candidates (TC) for testing.
  2. Unit Test. This a small independent program that is intended to re-use TC from the main program and apply tests for them.
  3. Test Processor. The set of approaches and tools that will be used to apply test for the environments from Project Configuration File platformio.ini.

Workflow

  1. Create PlatformIO project using platformio init command. For Desktop Unit Testing (on the host machine), need to use Platform native.

    ; PlatformIO Project Configuration File
    ;
    ;   Build options: build flags, source filter, extra scripting
    ;   Upload options: custom port, speed and extra flags
    ;   Library options: dependencies, extra library storages
    ;
    ; Please visit documentation for the other options and examples
    ; http://docs.platformio.org/en/stable/projectconf.html
    
    ;
    ; Embedded platforms
    ;
    
    [env:uno]
    platform = atmelavr
    framework = arduino
    board = uno
    
    [env:nodemcu]
    platform = espressif8266
    framework = arduino
    board = nodemcuv2
    
    ;
    ; Desktop platforms (Win, Mac, Linux, Raspberry Pi, etc)
    ; See http://platformio.org/platforms/native
    ;
    
    [env:native]
    platform = native
    
  2. Place source code of main program to src directory.

  3. Wrap main() or setup()/loop() methods of main program in UNIT_TEST guard:

    /**
    * Arduino Wiring-based Framework
    */
    #ifndef UNIT_TEST
    #include <Arduino.h>
    void setup () {
      // some code...
    }
    
    void loop () {
      // some code...
    }
    #endif
    
    /**
    * Generic C/C++
    */
    #ifndef UNIT_TEST
    int main(int argc, char **argv) {
      // setup code...
    
      while (1) {
          // loop code...
      }
      return 0
    }
    #endif
    
  4. Create test directory in the root of project. See test_dir.

  5. Write test using API. The each test is a small independent program with own main() or setup()/loop() methods. Also, test should start from UNITY_BEGIN() and finish with UNITY_END().

  6. Place test to test directory. If you have more than one test, split them into sub-folders. For example, test/test_1/*.[c,cpp,h], test_N/*.[c,cpp,h], etc. If no such directory in test folder, then PlatformIO Unit Testing Engine will treat the source code of test folder as SINGLE test.

  7. Run tests using platformio test command.

API

The summary of Unity Test API:

  • Running Tests
    • RUN_TEST(func, linenum)
  • Ignoring Tests
    • TEST_IGNORE()
    • TEST_IGNORE_MESSAGE (message)
  • Aborting Tests
    • TEST_PROTECT()
    • TEST_ABORT()
  • Basic Validity Tests
    • TEST_ASSERT_TRUE(condition)
    • TEST_ASSERT_FALSE(condition)
    • TEST_ASSERT(condition)
    • TEST_ASSERT_UNLESS(condition)
    • TEST_FAIL()
    • TEST_FAIL_MESSAGE(message)
  • Numerical Assertions: Integers
    • TEST_ASSERT_EQUAL_INT(expected, actual)
    • TEST_ASSERT_EQUAL_INT8(expected, actual)
    • TEST_ASSERT_EQUAL_INT16(expected, actual)
    • TEST_ASSERT_EQUAL_INT32(expected, actual)
    • TEST_ASSERT_EQUAL_INT64(expected, actual)
    • TEST_ASSERT_EQUAL_UINT(expected, actual)
    • TEST_ASSERT_EQUAL_UINT8(expected, actual)
    • TEST_ASSERT_EQUAL_UINT16(expected, actual)
    • TEST_ASSERT_EQUAL_UINT32(expected, actual)
    • TEST_ASSERT_EQUAL_UINT64(expected, actual)
    • TEST_ASSERT_EQUAL_HEX(expected, actual)
    • TEST_ASSERT_EQUAL_HEX8(expected, actual)
    • TEST_ASSERT_EQUAL_HEX16(expected, actual)
    • TEST_ASSERT_EQUAL_HEX32(expected, actual)
    • TEST_ASSERT_EQUAL_HEX64(expected, actual)
    • TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, elements)
    • TEST_ASSERT_EQUAL(expected, actual)
    • TEST_ASSERT_INT_WITHIN(delta, expected, actual)
  • Numerical Assertions: Bitwise
    • TEST_ASSERT_BITS(mask, expected, actual)
    • TEST_ASSERT_BITS_HIGH(mask, actual)
    • TEST_ASSERT_BITS_LOW(mask, actual)
    • TEST_ASSERT_BIT_HIGH(mask, actual)
    • TEST_ASSERT_BIT_LOW(mask, actual)
  • Numerical Assertions: Floats
    • TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual)
    • TEST_ASSERT_EQUAL_FLOAT(expected, actual)
    • TEST_ASSERT_EQUAL_DOUBLE(expected, actual)
  • String Assertions
    • TEST_ASSERT_EQUAL_STRING(expected, actual)
    • TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len)
    • TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, actual, message)
    • TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(expected, actual, len, message)
  • Pointer Assertions
    • TEST_ASSERT_NULL(pointer)
    • TEST_ASSERT_NOT_NULL(pointer)
  • Memory Assertions
    • TEST_ASSERT_EQUAL_MEMORY(expected, actual, len)