C/C++ source files shall be placed in src/ or include/. PlatformIO Unit Testing Engine does not build source code from src/ by default. To test library test code in src/, instruct PlatformIO to build files in src/ using test_build_project_src option in platformio.ini.

Directory Layout

.
├── library.json
├── platformio.ini
├── src
│   ├── HelloWorld.cpp
│   └── HelloWorld.h
└── test
    └── test_main.cpp

Library Manifest library.json

Create the manifest file library.json as usual.

{
  "name": "HelloWorld",
  "version": "1.0.0",
  "description": "A \"Hello world\" program is a computer program that outputs \"Hello World\" (or some variant) on a display device",
  "keywords": "planet, happiness, people",
  "repository":
  {
    "type": "git",
    "url": "https://git.example.com/john-smith/hello-world.git"
  },
  "authors":
  [
    {
      "name": "John Smith",
      "email": "me@john-smith.example.com",
      "url": "https://john-smith.example.com/contact"
    }
  ],
  "license": "MIT",
  "homepage": "https://helloworld.example.com/",
  "frameworks": "*",
  "platforms": "*"
}

platform.io

As described above, you need specify test_build_project_src to yes. To test on PC native by default, create native environment and set default_envs to native.

[platformio]
default_envs = native

[env]
monitor_flags = --echo
monitor_filters = time, send_on_enter, default
test_build_project_src = yes

[env:native]
platform = native

# create any other environment as you like...

[env:m5stack-atom]
platform = espressif32
board = m5stack-atom
framework = arduino
monitor_speed = 115200

[env:sparkfun_promicro16]
platform = atmelavr
board = sparkfun_promicro16
framework = arduino

Codes

In this example, we simply declare a function that returns a string “Hello, world.”

// src/HelloWorld.h
#ifndef _HELLO_WORLD_H_
#define _HELLO_WORLD_H_
//#pragma once

const char* HelloWorld(void);

#endif
// src/HelloWorld.cpp
#include "HelloWorld.h"

const char* HelloWorld(void) {
  return "Hello, World.";
}

Test codes

Write the test case as a function like the following:

#include <HelloWorld.h>
#include <unity.h>

#include <cstring>

void test_hello_world() {
  TEST_ASSERT_EQUAL(0, strcmp("Hello, World.", HelloWorld()));
}

Register this function with RUN_TEST() surrounded UNITY_BEGIN() and UNITY_END().

//#include <unity.h>
UNITY_BEGIN();
RUN_TEST(test_hello_world);
// RUN_TEST(test_another_one);
UNITY_END();

The entry point is different between arduino and native. setup() and loop() are required for arduino, main() for native.

To run it in the same source code, you need to switch entry point functions using #ifdef ARDUINO as shown below:

#include <HelloWorld.h>
#include <unity.h>

#include <cstring>

#ifdef ARDUINO
#include <Arduino.h>
#endif

void test_hello_world() {
  TEST_ASSERT_EQUAL(0, strcmp("Hello, World.", HelloWorld()));
}

#ifdef ARDUINO
void setup() {
  delay(2000); // add 2-sec wait for the board w/o software resetting via
               // Serial.DTR/RTS
#else
int main(int argc, char *argv[]) {
#endif
  UNITY_BEGIN();
  RUN_TEST(test_hello_world);
  UNITY_END();

#ifndef ARDUINO
  return 0;
#endif
}

#ifdef ARDUINO
void loop() {}
#endif

Run test

Just run pio test (or run the test task on the your favorite tool).

$ pio -c vim test
Verbose mode can be enabled via `-v, --verbose` option
Collected 1 items

Processing * in native environment
--------------------------------------------------------------------------------
Building...
Testing...
test/test_main.cpp:22:test_hello_world	[PASSED]

-----------------------
1 Tests 0 Failures 0 Ignored
OK
========================== [PASSED] Took 0.64 seconds ==========================

Test    Environment    Status    Duration
------  -------------  --------  ------------
*       native         PASSED    00:00:00.642
========================= 1 succeeded in 00:00:00.642 =========================

Same way for testing on a board, but it takes longer time.

$ pio -c vim test -e m5stack-atom
Verbose mode can be enabled via `-v, --verbose` option
Collected 1 items

Processing * in m5stack-atom environment
--------------------------------------------------------------------------------
Building...
Uploading...
Testing...
If you don't see any output for the first 10 secs, please reset board (press reset button)

test/test_main.cpp:22:test_hello_world	[PASSED]
-----------------------
1 Tests 0 Failures 0 Ignored
========================== [PASSED] Took 8.13 seconds ==========================

Test    Environment    Status    Duration
------  -------------  --------  ------------
*       m5stack-atom   PASSED    00:00:08.127
========================= 1 succeeded in 00:00:08.127 =========================

References