From e262cabc404a93ce60136af0086106bc532af65c Mon Sep 17 00:00:00 2001 From: Adrian Ancona Novelo Date: Tue, 29 Oct 2024 19:36:19 -0500 Subject: [PATCH] Finish unit testing code for esp32 draft (#28) --- ...2024-10-30-unit-testing-code-for-esp32.md} | 123 +++++++++++++++++- 1 file changed, 117 insertions(+), 6 deletions(-) rename _drafts/{unit-testing-code-for-esp32.md => 2024-10-30-unit-testing-code-for-esp32.md} (61%) diff --git a/_drafts/unit-testing-code-for-esp32.md b/_drafts/2024-10-30-unit-testing-code-for-esp32.md similarity index 61% rename from _drafts/unit-testing-code-for-esp32.md rename to _drafts/2024-10-30-unit-testing-code-for-esp32.md index d8af9fb..ae359dc 100644 --- a/_drafts/unit-testing-code-for-esp32.md +++ b/_drafts/2024-10-30-unit-testing-code-for-esp32.md @@ -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 @@ -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. @@ -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: @@ -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()': @@ -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_t event_loop(); +``` + +And this in `library-root/src/library.cpp`: + +```cpp +#include "library.hpp" + +#include + +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 + +#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).