Automating Reliability Engineering with Unit Testing, HIL Testing & EmbedOps – Part 1

Dev Containers

A key challenge for embedded systems developers is ensuring high-reliability operation in products designed for years-long deployments, sometimes in remote locations. A prime example of such a device is a Bluetooth-enabled environmental sensor node that performs functions such as advertising over Bluetooth Low Energy (BLE), transmitting temperature and humidity data, and supporting over-the-air (OTA) firmware updates and diagnostics.

In commercial markets, tens of thousands, hundreds of thousands, or more of these units ship over the course of the product lifecycle. Any failures in functionality or performance could have dire consequences for the manufacturer:

  • Time-to-market delays can lead to missed opportunities and lost revenue as competitors capture new customers by launching similar products.
  • Costly recalls erode profit as returned units are refunded, repaired, or replaced, depending on the nature of the failure.
  • Security vulnerabilities can cause service disruptions and data breaches that further damage brand reputation and the likelihood of repeat business with customers.

To avoid these pitfalls, development teams can leverage automation and modern engineering practices for faster ad-hoc testing and bug remediation. Still, this can be challenging when working with manual build processes across distributed teams using inconsistent toolchains. Dojo Five EmbedOps helps embedded teams quickly stand up unit testing, hardware‑in‑the‑loop (HIL) testing, and automated reporting to find and address operating deficiencies that could bring down a commercial product, whether during development or after deployment.

In part 1, we investigate how EmbedOps streamlines unit testing.

BLE Sensor Node Proof of Concept

Let’s build out the example BLE-enabled environmental sensor mentioned previously into something more real. We will use a Nordic nRF52 wireless system-on-a-chip (SoC) as a baseline and start by defining some of the system’s key functions. These include:

  • Periodically reading the temperature and humidity from a sensor
  • Flash an LED to indicate status
  • Using Nordic’s UART Service (NUS) for diagnostic echo

The EmbedOps DevKit Quickstart guide explains how to spin up a Zephyr‑based LED‑blink project that runs inside a dev container—this takes around 5 minutes to create after installing the EmbedOps CLI and required tools such as Docker.

Running the eo init command in a fresh repository will prompt users to select a preferred continuous integration (CI) platform (GitLab, GitHub, or Bitbucket) and the base container OS for the Linux distribution. Once confirmed, EmbedOps registers the project and creates the following to ensure all dev team members have the same tool versions:

  • .embedops/repo_id.yml — Connects your repo to the EmbedOps platform
  • .embedops/template-settings.yml — Saves your configuration choices
  • .devcontainer/ — The dev container configuration
  • CI configuration files for your chosen provider

Developers can then use eo add unittest to set up a unit testing framework based on Ceedling or Google Test. Alternatively, the eo add hil command is used to add HIL scaffolding. The EmbedOps CLI automatically downloads templates for test harnesses, CI scripts, and the required dev container features to conduct these tests.

eo add unittest, Step 1. Select the GoogleTest framework

eo add unittest, Step 2. Specify the source directory

eo add unittest, Step 3. Specify the test directory

eo add unittest, Step 4. Enable code coverage

eo add unittest, Step 5. The unit test template has been added successfully to the project

The EmbedOps HIL reference documentation also includes example projects such as the NUS Echo demo. This example shows the test procedure for a device that has NUS echo implemented, advertising as “EmbedOps HIL Demo Device” and echoing any data sent over NUS. The test sends data and asserts whether the received data is the same as what was sent, thereby verifying correct operation. (Note: HIL testing is explored in more detail in Part 2.)

Unit Testing

The next step is to write a unit test. Unit tests run off-target and offer fast evaluation of short sections of code—hundreds or thousands of unit tests can be conducted per second. These tests verify that each section of code performs as it should, returning the correct result for a given input.

Through unit testing, developers can catch programming and other errors, reducing the cost of fixes later in development or after deployment. In addition to localizing failures for easier troubleshooting, unit testing also allows firmware development to progress independently when hardware isn’t ready.

While embedded teams often use custom boards for hardware testing, relying purely on real-world testing slows development and blocks parallel work. To conduct effective off-target testing, firmware developers require detailed unit testing architectures and dependency injection—i.e., simulated hardware dependencies—when isolating code modules.

Additionally, different compilers, linkers, and build systems can produce test results that vary across different host machines. The key advantage of using dev container environments created by EmbedOps is that it standardizes development toolchains across the team to ensure reproducible test results.

Developers may perceive unit tests as extra work or struggle to recreate hardware dependencies when isolating firmware modules. To ease the adoption of this development best practice, the eo add unittest template creates the required scaffolding for detailed unit testing and shows examples to get developers started.

The below code snippet is a unit test covering the conversion of Zephyr sensor subsystem values into values for the respective sensor type (pressure in this example). struct sensor_value is mocking the Zephyr sensor API, allowing us to test the functionality.

For example, consider creating a test for the conversion of Zephyr sensor subsystem values into unit values for the respective sensor value types (pressure in this example). For simplicity, the tests do not include the zephyr subsystem linking it to the tests would be an unnecessary complication considering what we need to test.

The definition of the struct sensor_value is a mocking of the Zephyr sensor API allowing us to test the functionality.

struct sensor_value {                                                                                                  
  int32_t val1;
  int32_t val2;
};

void setUp(void) {}

void tearDown(void) {}

void test_pressure_conversion(void) {
  struct sensor_value val;

  val.val1 = 1;
  val.val2 = 500000;
  TEST_ASSERT_EQUAL_INT32(1500, sensor_value_to_pressure(&val));

  val.val1 = 0;
  val.val2 = 0;
  TEST_ASSERT_EQUAL_INT32(0, sensor_value_to_pressure(&val));

  val.val1 = -1;
  val.val2 = -500000;
  TEST_ASSERT_EQUAL_INT32(0, sensor_value_to_pressure(&val));

  val.val1 = 1000;
  val.val2 = 999000;
  TEST_ASSERT_EQUAL_INT32(1000999, sensor_value_to_pressure(&val));

  val.val1 = 0;
  val.val2 = 999;
  TEST_ASSERT_EQUAL_INT32(0, sensor_value_to_pressure(&val));

  val.val1 = 0;
  val.val2 = 1000;
  TEST_ASSERT_EQUAL_INT32(1, sensor_value_to_pressure(&val));
}

Unit testing frameworks like Ceedling or Google Test can be used to generate a JUnit XML report by enabling the junit_tests_report plugin in EmbedOps.

1. First, execute eo test to run all unit tests. The CLI compiles the code inside the container and reports pass/fail results. For large projects, eo test –optimize runs only the tests affected by uncommitted changes, which can save time during development.

Running unit test locally on the host machine

2. At the end of the unit test job, call eo report unittest to upload the test report (e.g., the JUnit XML report for Ceedling). This will push the pass/fail metrics to the EmbedOps platform. The web app then tracks unit test counts and trends over time for a detailed understanding of firmware quality over the development cycle.

Running unit test on GitLab CI pipeline

The Branches page on the EmbedOps platform shows the passing test

The Unit Tests page on the EmbedOps platform shows the unit test trends over time

The Consequences of Insufficient Testing

Unfortunately, there are numerous instances of commercial Bluetooth products reaching the market and failing as a result of errors that could have been prevented with sufficient unit testing. The Samsung Mobile Exynos 7885 and even Bluetooth stacks, drivers, and software development kits (SDKs) from embedded technology suppliers like Espressif Systems, TI, Microchip, and others are among them.

This roster of household name technology brands should be both comforting and cautionary. Poor testing, or not testing at all, is an industry-wide problem that plagues everyone from Fortune 500 organizations down to small teams of contractors.

EmbedOps helps build robust testing practices into development workflows by providing an intuitive, modern development infrastructure that “shifts left” testing into the development process. Dojo Five and its partners have successfully leveraged the EmbedOps framework, methodologies, and practices in countless commercial embedded products, including BLE-enabled drones, medical devices, clinical wearables, and more.

Takeaways

This article has covered several discussions of how the Dojo Five EmbedOps platform can streamline testing for verified operating quality:

  1. EmbedOps creates consistent build and test containers to eliminate “works on my machine” problems that are common across distributed development teams.
  2. The eo add unittest command is used to scaffold Ceedling and Google Test testing tools. Developers should run tests frequently using the eo test command, and these test runs can be optimized with –optimize.
  3. Unit tests can check that a function returns the expected result for a given input.  For a function that restructures data format, for example, it tests that for a known set of input data, the code returns the data in the correct new structure or produces errors correctly.
  4. In embedded systems, especially, many things can’t be directly addressed through I/O-independent unit tests, such as data acquisition functionality. Here, developers must use mocks, fakes, and stubs to isolate any dependencies before unit testing—a section in this blog explains these test doubles in full.
  5. When unit tests reach their limits, HIL tests offer the next step for testing code performance using real-world hardware and data interactions.

Conclusion

Unit testing may seem daunting at first, but Dojo Five EmbedOps streamlines the process for consistent results across distributed teams. With a BLE sensor application example, this article has shown just how quickly teams can set up these practices using their preferred CI platform and testing framework to dramatically improve the quality of embedded products.

For developers looking to get hands-on experience with the EmbedOps platform, Dojo Five offers its EmbedOps DevKit—available from the Dojo Five team. After running through the DevKit Quickstart guide, developers can explore other EmbedOps templates, such as memory analysis and cppcheck, that support additional debugging and analysis during development.

By enabling easy adoption of thorough testing and reporting procedures, EmbedOps helps embedded teams build confidence when commercializing their products. Moreover, this testing streamlines certification so that when customers receive products, quality and reliability are assured.

Additional Resources

Discover why Dojo Five EmbedOps is the embedded enterprise choice for build tool and test management.

Sign up to receive a free account to the EmbedOps platform and start building with confidence..

  • Connect a repo
  • Use Dev Containers with your Continuous Integration (CI) provider
  • Analyze memory usage
  • Integrate and visualize static analysis results
  • Perform Hardware-in-the-Loop (HIL) tests
  • Install the Command Line Interface for a developer-friendly experience

Subscribe to our Monthly Newsletter

Subscribe to our monthly newsletter for development insights delivered straight to your inbox.

Interested in learning more?

Best-in-class embedded firmware content, resources and best practices

HIL Raspberry Pi

Hardware-in-the-loop is hard! Sometimes.

By: Ander Galisteo, Ph.D.
The concept of hardware-in-the-loop (HIL) is not new in the embedded space. The basic idea is to automate the functional tests of your device under development to verify if your implementation does what it’s supposed to do.

Read More »
Automating Reliability Engineering with Unit Testing, HIL Testing & EmbedOps, Part 1

Automating Reliability Engineering with Unit Testing, HIL Testing & EmbedOps, Part 1

A key challenge for embedded systems developers is ensuring high-reliability operation in products designed for years-long deployments, sometimes in remote locations. A prime example of such a device is a Bluetooth-enabled environmental sensor node that performs functions such as advertising over Bluetooth Low Energy (BLE), transmitting temperature and humidity data, and supporting over-the-air (OTA) firmware updates and diagnostics. In commercial markets, tens of thousands, hundreds of thousands, or more of these units ship over the course of the product lifecycle.

Read More »
Dojo Five Embedded Reading List 2025

What is Dojo Five Reading in April 2025?

“Dojo” translates to “place of the way” and represents an environment for immersive learning. Here at Dojo 5, we prioritize that learning by encouraging our engineers to always be on the look out for the latest news, new technologies, and new tools that help us modernize the firmware product development and deployment experience for our clients. To help other engineers expand their knowledge as well, here’s a collection of interesting blogs, news, and tools that our engineers have been reading

Read More »