📜 ⬆️ ⬇️

Unit tests on C - nothing is easier

After reading the article "Testing Embedded Systems" and comments to it, I was somewhat struck by the fact that many habrovans are familiar with the book "Test Driven Development for Embedded C (Pragmatic Programmers)" and the Unity framework, but do not use the entire arsenal of tools that offer guys from throwtheswitch.org .

I want to briefly share the experience of using these same tools.

About myself


It so happened that I gained my experience in programming embedded systems through tests (Unit, Integration, System, Stress). For three years I was lucky enough to go all the way from Junior and writing tests covering the code of other specialists to a Senior with experience in developing systems using TDD methodology.

The promised


The above Unity framework is very simple and easy to use. But this is just the tip of the iceberg. The throwtheswitch.org page has the following tools.
')
CMock is a tool that allows you to automatically generate the C code of mocks for your tests. Written in Ruby. I affirm that the person who “generated” mock-hands for three years is just a gift for a C-developer. But to use it autonomously without the next tool, in my opinion, is not rational.

Ceedling is a whole build system, according to the authors themselves. But in fact - that's all you need to work. This package contains everything you need: Unity (“test runners” and “checker” values), CMock (mock generator) and command line support through ruby ​​make.

Other - under this strange heading there is a very, useful, in my opinion, tool - CException. An incredibly small library for C allowing you to get some semblance of exceptions. But I will not misinform. In projects it was not possible to use.

The only thing that leaves much to be desired in this variety of beautiful things is the tutorial. His, however, and no. Everything is clear, but where to begin for a beginner is a big question. I'll try to fix the situation.

First of all, Ceedling should be correctly installed and tested for operability as indicated here .

After installation, create a folder and test environment of the project with the command:

ceedling new MyNewProject 

As a result, a folder MyNewProject will be created within which the following folders and files will be generated:


It's time to write the first test.

Place the following test_calc.c file in the test folder:

 #include "unity.h" #include "calc.h" void setUp(void) { } void tearDown(void) { } void test_add( void ) { int result = 0; result = calc_add(2,2); TEST_ASSERT_EQUAL_INT( 4, result ); } 

Run the test with the command:

 ceedling test:test_calc.c 

The result is expected. There is a test, there is no code. The project can not be assembled.

Add the code.
Put two files in the src folder:

"Calc.h"
 #ifndef CALC_H #define CALC_H int calc_add(int a, int b); #endif 

"Calc.c"
 #include "calc.h" int calc_add(int a, int b) { return a + b; } 

Repeat the build and try to run the test:

 ceedling test:test_calc.c 

If everything is done correctly, then the test results should be in the console:

 Test 'test_calc.c' ------------------ Compiling test_calc_runner.c... Compiling test_calc.c... Compiling calc.c... Compiling unity.c... Compiling cmock.c... Linking test_calc.out... Running test_calc.out... ------------------------- OVERALL UNIT TEST SUMMARY ------------------------- TESTED: 1 PASSED: 1 FAILED: 0 IGNORED: 0 

This short example shows that the test-runner was generated and added to the assembly automatically. Its code can be found in the build / test / runners folder.

Let's try to complicate the task and suppose that our “combat” file should be able to count only under a certain condition, which is checked in another software module (for example, rules.c). Modify the code to illustrate:

"Calc.c"
 #include "calc.h" #include "rules.h" int calc_add(int a, int b) { if (rules_is_addition_allowed()) { return a + b; } return 0; } 

Add another file to the src folder:

"Rules.h"
 #ifndef RULES_H #define RULES_H int rules_is_addition_allowed(void); #endif 

An attempt to run the test will fail, because there is no definition for the rules_is_addition_allow () function.

It's time to use CMock.
Change the test as follows:

 #include "unity.h" #include "calc.h" #include "mock_rules.h" void setUp(void) { } void tearDown(void) { } void test_add( void ) { int result = 0; rules_is_addition_allowed_ExpectAndReturn(1); result = calc_add(2,2); TEST_ASSERT_EQUAL_INT( 4, result ); } void test_add_off_nominal( void ) { int result = 0; rules_is_addition_allowed_ExpectAndReturn(0); result = calc_add(2,2); TEST_ASSERT_EQUAL_INT( 0, result ); } 

Thus, we obtained the automatically generated mock only by specifying "#include" mock_rules.h ". The source code of this file can be found in the build / test / mocks directory. Studying it will give a good idea of ​​how to change the behavior of the module being replaced.

Reservations


1. I use this framework only for testing code on a PC. This dictates certain rules for the architecture of the software being developed. I do not see sense to drive unit tests on real hardware. HAL - it either works or not and is tested manually (my vision of the situation);
2. I do not use this framework for testing multiple threads. The flow safety of this tool was not investigated by me;
3. This article does not teach how to write code and / or tests, but only gives a brief idea about the development tools mentioned above.

Source: https://habr.com/ru/post/244835/


All Articles