Natalia Pluta

Mastering Zephyr RTOS Testing: Getting Started with Twister

Friday, February 27, 2026

Introduction

In the world of embedded systems, manual testing is often the bottleneck that slows down innovation. As projects grow in complexity - supporting multiple architectures and hardware configurations - ensuring stability becomes an uphill battle. This is where Twister comes in.

Twister is not just a script; it is the backbone of the Zephyr Project’s quality assurance. Whether you are validating a simple driver or a complex multi-threaded application, Twister provides a scalable, automated way to ensure your code works across the vast ecosystem of Zephyr-supported boards. In this guide, we will move beyond manual compilation and show you how to leverage Twister to streamline your testing workflow from day one.

Author's Note: This article was written with the support of Zephyr 4.3.0 (Source: GitHub v4.3.0, Documentation). However, it is highly probable that the concepts and commands will work perfectly fine with older and newer versions of the RTOS.

Building and Running Your First Zephyr Tests with Twister

Welcome to the first part of our Mastering Zephyr RTOS Testing series. Before writing any custom C code or integration scripts, you need to understand the beating heart of Zephyr's testing ecosystem: Twister.

Prerequisites: Before diving in, we assume you already have a functional Zephyr workspace set up. If you haven't done this yet, please follow the Zephyr 4.3.0 Getting Started Guide.

What is Twister?

Twister is Zephyr's integrated test execution framework that discovers, builds, and executes tests across multiple platforms. Think of it as your single entry point for running any kind of Zephyr test. Instead of manually compiling binaries and flashing them to a board to see if a feature works, Twister automates the entire pipeline.

How Twister Works Under the Hood

When you invoke Twister, it orchestrates a comprehensive end-to-end execution pipeline:

  1. Discovery - scans directories for testcase.yaml or sample.yaml files.

  2. Filtering - applies platform, architecture, tag, and filter-expression constraints.

  3. Building - compiles each test scenario for each matching platform (in parallel).

  4. Execution - runs the binary on the target (simulators, real HW, etc.).

  5. Evaluating - uses the configured harness to parse output and determine pass/fail.

  6. Reporting - generates JSON, JUnit XML, and optional coverage reports.

For a full architectural reference, you can check the Twister Documentation.

The Anatomy of a Test: testcase.yaml and sample.yaml

Before you can run a test, Twister needs to know it exists. Twister discovers tests by recursively scanning your directories for testcase.yaml or sample.yaml files.

The testcase.yaml File

The testcase.yaml file defines test scenarios and their configuration. Here is an overview of some of the available options:

tests:
  # Scenario name - must be unique
  my_subsystem.feature.test_basic:

    # Tags for filtering
    tags:
      - drivers
      - uart

    # Test harness selection
    harness: ztest     # ztest | pytest | console | gtest | robot | shell | ctest | power | bsim

    # Platform restrictions
    platform_allow:
      - native_sim
      - nrf52840dk/nrf52840
    platform_exclude:
      - qemu_x86

    # Architecture restrictions
    arch_allow: [arm, x86]

    # Resource requirements
    min_ram: 64      # KB
    min_flash: 128   # KB

Note: While ztest is the default C testing framework, we will explore other harnesses like pytest, console, and robot in subsequent articles.

The sample.yaml File

While testcase.yaml is used for formal test suites, Zephyr uses sample.yaml to build and verify application samples. Its structure is very similar to a test case, often relying on the console harness to match simple text output to verify the sample built and ran correctly:

sample:
  name: Hello World Sample
  description: A basic sample that prints "Hello World"
tests:
  sample.basic.hello_world:
    tags:
      - sample
      - introduction
    harness: console
    harness_config:
      type: one_line
      regex:
        - "Hello World"

Filter Expressions

Within both of your YAML files, you can define filter expressions that enable conditional test execution based on Kconfig, devicetree, and the environment. This ensures tests only run on boards that actually support the required hardware features or architectures:

# Kconfig-based
filter: CONFIG_UART_ASYNC_API and not CONFIG_DEBUG

# Devicetree-based
filter: dt_compat_enabled("nordic,nrf-uarte")
filter: dt_nodelabel_enabled("i2c0")

# Architecture and platform
filter: ARCH in ["arm", "riscv"] and PLATFORM != "qemu_x86"

Virtual Test Platforms: native_sim vs unit_testing

You don't need physical hardware to start testing in Zephyr. In fact, most of your day-to-day testing should be done on your host machine. Zephyr provides two primary virtual platforms for this, and it is crucial to understand the difference between them:

  • native_sim: Recommended for most unit and integration tests. It provides the full Zephyr OS, so modules that use logging, drivers, or other OS features work correctly. You can mock only the specific dependencies you want to isolate.

  • unit_testing: Used only for pure utility functions with zero OS dependencies (e.g., CRC calculation, string parsing). This board provides a minimal POSIX environment without the Zephyr kernel.

For a deeper dive into configuring tests specifically for this minimal environment (unit_testing), check out the Zephyr Unit Testing Quick Start Guide.

Running Your First Zephyr Tests

Let's run some real tests using Zephyr's built-in kernel test suite. To avoid compiling the entire OS (which would take a very long time!), we will narrow our command down to just the threading subsystem.

Testing on native_sim

To build and run tests that require the full Zephyr OS (like the threading subsystem), we use the native_sim board. Execute the following command from your Zephyr repository root:

west twister -T tests/kernel/threads/ -p

You should see an output similar to this, detailing the build progress, filtered tests, and final pass rates:

❯ west twister -T tests/kernel/threads/ -p native_sim
Renaming previous output directory to /home/npluta/zephyrproject/zephyr/twister-out.38
INFO    - Using Ninja..
INFO    - Zephyr version: v4.3.0
INFO    - Using 'zephyr' toolchain.
INFO    - Building initial testsuite list...
INFO    - Writing JSON report /home/npluta/zephyrproject/zephyr/twister-out/testplan.json
INFO    - JOBS: 12
INFO    - Adding tasks to the queue...
INFO    - Added initial list of jobs to queue
INFO    - Total complete:    1/  13   7%  built (not run):    0, filtered:    6, failed:    0, error:   INFO    - Total complete:    2/  13  15%  built (not run):    0, filtered:    7, failed:    0, error:   INFO    - Total complete:    3/  13  23%  built (not run):    0, filtered:    7, failed:    0, error:   INFO    - Total complete:    4/  13  30%  built (not run):    0, filtered:    7, failed:    0, error:   INFO    - Total complete:    5/  13  38%  built (not run):    0, filtered:    7, failed:    0, error:   INFO    - Total complete:    6/  13  46%  built (not run):    0, filtered:    7, failed:    0, error:   INFO    - Total complete:    7/  13  53%  built (not run):    0, filtered:    7, failed:    0, error:   INFO    - Total complete:    8/  13  61%  built (not run):    0, filtered:    7, failed:    0, error:   INFO    - Total complete:    9/  13  69%  built (not run):    0, filtered:    7, failed:    0, error:   INFO    - Total complete:   10/  13  76%  built (not run):    0, filtered:    7, failed:    0, error:   INFO    - Total complete:   11/  13  84%  built (not run):    0, filtered:    7, failed:    0, error:   INFO    - Total complete:   12/  13  92%  built (not run):    0, filtered:    7, failed:    0, error:   INFO    - Total complete:   13/  13  100%  built (not run):    0, filtered:    7, failed:    0, error:    0
INFO    - 18 test scenarios (18 configurations) selected, 7 configurations filtered (5 by static filter, 2 at runtime).
INFO    - 11 of 11 executed test configurations passed (100.00%), 0 built (not run), 0 failed, 0 errored, with no warnings in 43.32 seconds.
INFO    - 55 of 55 executed test cases passed (100.00%) on 1 out of total 1259 platforms (0.08%).
INFO    - 26 selected test cases not executed: 26 skipped.
INFO    - 11 test configurations executed on platforms, 0 test configurations were only built.
INFO    - Saving reports...
INFO    - Writing JSON report /home/npluta/zephyrproject/zephyr/twister-out/twister.json
INFO    - Writing xunit report /home/npluta/zephyrproject/zephyr/twister-out/twister.xml...
INFO    - Writing xunit report /home/npluta/zephyrproject/zephyr/twister-out/twister_report.xml...
INFO    -

Testing on unit_testing

If you want to run tests strictly on the minimal POSIX board to validate pure utilities, you can utilize the unit_testing board. Tests dedicated to this platform are gathered under the tests/unit/ directory in Zephyr. For example, you can execute the following command to test the CRC utility functions:

west twister -T tests/unit/crc/ -p

The output is often much faster since it doesn't need to compile the full RTOS:

❯ west twister -T tests/unit/crc/ -p unit_testing
Renaming previous output directory to /home/npluta/zephyrproject/zephyr/twister-out.39
INFO    - Using Ninja..
INFO    - Zephyr version: v4.3.0
INFO    - Using 'zephyr' toolchain.
INFO    - Building initial testsuite list...
INFO    - Writing JSON report /home/npluta/zephyrproject/zephyr/twister-out/testplan.json
INFO    - JOBS: 12
INFO    - Adding tasks to the queue...
INFO    - Added initial list of jobs to queue
INFO    - Total complete:    1/   2  50%  built (not run):    0, filtered:    0, failed:    0, error:   INFO    - Total complete:    2/   2  100%  built (not run):    0, filtered:    0, failed:    0, error:    0
INFO    - 2 test scenarios (2 configurations) selected, 0 configurations filtered (0 by static filter, 0 at runtime).
INFO    - 2 of 2 executed test configurations passed (100.00%), 0 built (not run), 0 failed, 0 errored, with no warnings in 3.79 seconds.
INFO    - 32 of 32 executed test cases passed (100.00%) on 1 out of total 1259 platforms (0.08%).
INFO    - 2 test configurations executed on platforms, 0 test configurations were only built.
INFO    - Saving reports...
INFO    - Writing JSON report /home/npluta/zephyrproject/zephyr/twister-out/twister.json
INFO    - Writing xunit report /home/npluta/zephyrproject/zephyr/twister-out/twister.xml...
INFO    - Writing xunit report /home/npluta/zephyrproject/zephyr/twister-out/twister_report.xml...
INFO    -

Controlling Twister Execution

Twister provides immense command-line flexibility for managing platforms, filters, builds, and outputs.

Tip: The commands below use -T tests/kernel/threads/ as a safe, fast example. You can replace this path with your own application's test directory.

Platform Selection

You can target one or multiple platforms, or filter by architecture:

# Single platform
west twister -T tests/kernel/threads/ -p native_sim

# Multiple platforms
west twister -T tests/kernel/threads/ -p native_sim -p qemu_cortex_m3

# Filter by architecture

Test Selection and Filtering

Twister lets you narrow things down by tags or patterns:

# Run one specific test scenario by its scenerio name
west twister -p native_sim -s kernel.threads.apis

# Filter by tags
west twister -T tests/kernel/threads/ -t timer -t kernel -p native_sim

# Regex pattern on test names
west twister -T tests/kernel/threads/ --test-pattern ".*init.*" -p native_sim

# Run only previously failed tests

Build Control

Sometimes you only want to verify the code compiles without executing it:

# Build only, no execution
west twister -T tests/kernel/threads/ -p native_sim -b

# Incremental build (reuse build directory)

Output and Reporting

Twister's reporting is highly customizable:

# Custom output directory
west twister -T tests/kernel/threads/ -p native_sim -O my_output/

# Verbose output (repeatable: -v, -vv, -vvv)

Parallel Execution

When running tests in Continuous Integration (CI), speed and reliability are key:

# Parallel builds (defaults to CPU count)
west twister -T tests/kernel/threads/ -p native_sim -j 8

# Subset testing for CI pipelines (run 1st of 5 subsets)
west twister -T tests/kernel/threads/ -p native_sim -B 1/5

# Retry failed tests

Executing tests on real hardware

Simulators are fantastic, but eventually, you need to prove your code works on real hardware. Twister allows you to target physical hardware by specifying a custom flash command or passing device-testing flags.

For example, to test the built-in hello_world sample on a physical Nordic nRF52840 Development Kit, use the following command:

west twister -T samples/hello_world \
    --device-testing \
    --device-serial /dev/ttyACM0 \
    --device-serial-baud 115200 \
    -p

Note: You can easily replace nrf52840dk/nrf52840 with the board identifier for your hardware. You can check available boards by running west boards or via the interactive searcher in the Zephyr 4.3.0 Board Documentation.

Because this command targets real hardware, Twister will provide a Hardware distribution summary showing exactly which physical boards passed or failed:

❯ west twister -T samples/hello_world \
    --device-testing \
    --device-serial /dev/ttyACM0 \
    --device-serial-baud 115200 \
    -p nrf52840dk/nrf52840
Renaming previous output directory to /home/npluta/zephyrproject/zephyr/twister-out.40
INFO    - Using Ninja..
INFO    - Zephyr version: v4.3.0
INFO    - Using 'zephyr' toolchain.
INFO    - Building initial testsuite list...
INFO    - Writing JSON report /home/npluta/zephyrproject/zephyr/twister-out/testplan.json
Device testing on:
| Platform            | ID   | Serial device   |
|---------------------|------|-----------------|
| nrf52840dk/nrf52840 |      | /dev/ttyACM0    |
INFO    - JOBS: 12
INFO    - Adding tasks to the queue...
INFO    - Added initial list of jobs to queue
INFO    - Total complete:    1/   1  100%  built (not run):    0, filtered:    0, failed:    0, error:    0
INFO    - 1 test scenarios (1 configurations) selected, 0 configurations filtered (0 by static filter, 0 at runtime).
INFO    - 1 of 1 executed test configurations passed (100.00%), 0 built (not run), 0 failed, 0 errored, with no warnings in 13.01 seconds.
INFO    - 1 of 1 executed test cases passed (100.00%) on 1 out of total 1259 platforms (0.08%).
INFO    - 1 test configurations executed on platforms, 0 test configurations were only built.
Hardware distribution summary:
| Board               | ID   |   Counter |   Failures |
|---------------------|------|-----------|------------|
| nrf52840dk/nrf52840 |      |         1 |          0 |
INFO    - Saving reports...
INFO    - Writing JSON report /home/npluta/zephyrproject/zephyr/twister-out/twister.json
INFO    - Writing xunit report /home/npluta/zephyrproject/zephyr/twister-out/twister.xml...
INFO    - Writing xunit report /home/npluta/zephyrproject/zephyr/twister-out/twister_report.xml...
INFO    -

Conclusion

Mastering Twister is the first step toward professional-grade Zephyr development. By moving away from manual "flash-and-hope" debugging and toward automated test suites, you ensure that your code remains robust even as the underlying RTOS evolves.

In this guide, we've covered how Twister discovers tests, the difference between virtual platforms, and how to execute tests on both simulators and real hardware. See you in the next guide!

Contact us

Address

400 Union Ave. SE,

Suite 200 

Olympia, WA 98501

Instant Messenger
Social Media

Contact us

Address

400 Union Ave. SE,

Suite 200 

Olympia, WA 98501

Instant Messenger
Social Media

Contact us

Address

400 Union Ave. SE,

Suite 200 

Olympia, WA 98501

Instant Messenger
Social Media