Skip to content

Commit

Permalink
Finish unit testing code for esp32 draft (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
soonick authored Oct 30, 2024
1 parent 8c9483a commit e262cab
Showing 1 changed file with 117 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
title: Unit Testing Code for ESP32
author: adrian.ancona
layout: post
# date: 2024-10-23
# permalink: /2024/10/unit-testing-code-for-esp32/
date: 2024-10-30
permalink: /2024/10/unit-testing-code-for-esp32/
tags:
- esp32
- programming
Expand All @@ -18,7 +18,7 @@ There are a few ways we can go about unit testing code written for ESP32:
- Run tests using an emulator
- Run tests on Linux host using mocks

In this article we are going to learn how to write tests that can run on a Linux host, so it's easy to plug them to a CI system.
We are going to learn how to write tests that can run on a Linux host, so it's easy to plug them to a CI system.

<!--more-->

Expand All @@ -39,7 +39,7 @@ library-root/
   └── ...
```

We are going to use the [Catch2](https://github.com/catchorg/Catch2) to run our unit tests because we just need to download a single file from Github. Copy the contents of [catch.hpp](https://github.com/catchorg/Catch2/blob/v2.x/single_include/catch2/catch.hpp) into `library-root/test/external/catch2/catch.hpp`.
We are going to use [Catch2](https://github.com/catchorg/Catch2) to run our unit tests because we just need to download a single file from Github. Copy the contents of [catch.hpp](https://github.com/catchorg/Catch2/blob/v2.x/single_include/catch2/catch.hpp) into `library-root/test/external/catch2/catch.hpp`.

We can create `library-root/test/CMakeLists.txt` to run our tests:

Expand Down Expand Up @@ -143,7 +143,7 @@ TEST_CASE("parseQueryString") {
}
```
If we run compile and run our tests now, we will get an error, because we haven't defined `parse_query_string`:
If we compile and run our tests now, we will get an error, because we haven't defined `parse_query_string`:
```
/library-root/test/src/test-main.cpp: In function 'void C_A_T_C_H_T_E_S_T_0()':
Expand Down Expand Up @@ -212,8 +212,119 @@ parse_query_string(const std::string &line) {
}
```
At this point our library doesn't use any esp-idf specific functionality, but it can be used by esp-idf projects and we can write tests for it.
At this point, our library doesn't use any esp-idf specific functionality, but it can be used by esp-idf projects, and we already wrote a test for it.
## Mocking esp-idf
When our code depends on esp-idf, we will need to mock the functionality in order to test it.
Let's say we have this code in `library-root/src/library.hpp`:
```cpp
#pragma once
#include <esp_err.h>
esp_err_t event_loop();
```

And this in `library-root/src/library.cpp`:

```cpp
#include "library.hpp"

#include <esp_event.h>

esp_err_t event_loop() {
return esp_event_loop_create_default();
}
```

We have introduced 2 ESP-IDF dependencies: `esp_err.h` and `esp_event.h`. To be able to write tests for this code, we will need to mock those dependencies.

We'll start by creating `library-root/test/mock/` folder. This will be the place where we'll add our mocks. We have the freedom to make our mocks do whatever we desire.

For this example, we'll keep them very simple.

library-root/test/mock/esp_err.h:

```cpp
#pragma once

#define ESP_OK 0

typedef int esp_err_t;
```

library-root/test/mock/esp_event.h:

```cpp
#pragma once

#include "esp_err.h"

esp_err_t esp_event_loop_create_default();
```

library-root/test/mock/esp_event.cpp:

```cpp
#include "esp_event.h"

esp_err_t esp_event_loop_create_default() {
return ESP_OK;
}
```

We also need to update `library-root/test/CMakeLists.txt`, so it knows where to find the mocks:

```cpp
cmake_minimum_required(VERSION 3.27.4)
project(LibraryTest
VERSION 0.1.0
LANGUAGES CXX)

include_directories(external/catch2)
include_directories(../include)
include_directories(mock)

FILE(GLOB MOCK_SRCS mock/*.cpp)

set(TESTING_SRCS
../src/library.cpp
)

set(TEST_TARGET_SRCS
src/test-main.cpp
)

add_compile_options(-Wall -Wextra -Wpedantic -Werror)

add_executable(
test
${MOCK_SRCS}
${TEST_TARGET_SRCS}
${TESTING_SRCS}
)
```

Now, we can write a test:

```cpp
#define CATCH_CONFIG_MAIN
#include <catch.hpp>

#include "library.hpp"

TEST_CASE("event_loop") {
SECTION("Returns ESP_OK") {
REQUIRE(event_loop() == ESP_OK);
}
}
```
## Conclusion
Writing tests for ESP-IDF is not very complicated once we know what to do. The biggest hurdle is probably writing the tests, which, depending on the amount of dependencies, might be a lot of work.
As usual, you can find a working example in [my examples' repo](https://github.com/soonick/ncona-code-samples/tree/master/unit-testing-code-for-esp32).

0 comments on commit e262cab

Please sign in to comment.