0


Unity(单元测试)在STM32上的移植与应用

概述

Unity Test是一个为C构建的单元测试框架。本文基于STM32F407为基础,完全使用STM32CubeIDE进行开发,移植和简单使用Unity。

单片机型号:STM32F407VET6

软件:STM32CubeIDE Version: 1.14.1 Unity Version:2.6.0

一、配置stm32工程

新建工程,选择407芯片,生成工程后开始配置硬件,这里我们只使用了串口1,作为打印输出串口,按照图片设置,波特率等根据需要设置,这里我使用的默认值。

我习惯单独生成c和h文件。

保存生成代码。

在main.c文件中添加串口重定向函数

  1. /* Private user code ---------------------------------------------------------*/
  2. /* USER CODE BEGIN 0 */
  3. int __io_putchar(int ch)
  4. {
  5. /* Implementation of __io_putchar */
  6. /* e.g. write a character to the UART1 and Loop until the end of transmission */
  7. HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFFFFFF);
  8. return ch;
  9. }
  10. int __io_getchar(void)
  11. {
  12. /* Implementation of __io_getchar */
  13. char rxChar;
  14. // This loops in case of HAL timeout, but if an ok or error occurs, we continue
  15. while (HAL_UART_Receive(&huart1, (uint8_t *)&rxChar, 1, 0xFFFFFFFF) == HAL_TIMEOUT);
  16. return rxChar;
  17. }
  18. /* USER CODE END 0 */

此时理论上可以编译通过,并且printf好用。

二、下载unity源码

GitHub - ThrowTheSwitch/Unity: Simple Unit Testing for C

源码地址如上。

在工程根路径下创建Unity文件夹,并将unity源码中的src文件夹中的文件放到这个目录中。并将example文件夹中的unity_config.h也放到目录中。

在工程中右键并刷新

这时就会看到我们添加的文件夹了。

在工程上右键Properties,如下图步骤添加unity到includes目录中。

如上图,添加源文件到工程中参与编译。

此时就可以编译了,可以正常编译通过。

这里说一下配置文件,unity_config.h,如果使用的是stm32的cube,那么这个文件什么都不用写,如果使用的是其他mcu或者编译器,需要详细阅读并进行相关的宏定义。

三、测试程序

下面我先简单翻译一下github上的readme。

基本有效性测试

TEST_ASSERT_TRUE(condition)
如果条件结果为 false,则失败
TEST_ASSERT_FALSE(condition)
如果条件结果为 true,则失败
TEST_ASSERT(condition)
另一种方式TEST_ASSERT_TRUE
TEST_ASSERT_UNLESS(condition)
另一种方式TEST_ASSERT_FALSE
TEST_FAIL()
TEST_FAIL_MESSAGE(message)
此测试会自动标记为失败。 输出消息,说明原因。

数值断言:整数

TEST_ASSERT_EQUAL_INT(expected, actual)
TEST_ASSERT_EQUAL_INT8(expected, actual)
TEST_ASSERT_EQUAL_INT16(expected, actual)
TEST_ASSERT_EQUAL_INT32(expected, actual)
TEST_ASSERT_EQUAL_INT64(expected, actual)
比较两个整数是否相等,并将错误显示为有符号整数。 将根据您的自然整数大小执行强制转换, 当您需要指定确切的大小时,您可以使用特定版本。
TEST_ASSERT_EQUAL_UINT(expected, actual)
TEST_ASSERT_EQUAL_UINT8(expected, actual)
TEST_ASSERT_EQUAL_UINT16(expected, actual)
TEST_ASSERT_EQUAL_UINT32(expected, actual)
TEST_ASSERT_EQUAL_UINT64(expected, actual)
比较两个整数是否相等,并将错误显示为无符号整数。 与 INT 一样,也有不同大小的变体。
TEST_ASSERT_EQUAL_HEX(expected, actual)
TEST_ASSERT_EQUAL_HEX8(expected, actual)
TEST_ASSERT_EQUAL_HEX16(expected, actual)
TEST_ASSERT_EQUAL_HEX32(expected, actual)
TEST_ASSERT_EQUAL_HEX64(expected, actual)
比较两个整数是否相等,并将错误显示为十六进制。与其他整数比较一样,您可以指定大小。。。这里的大小也将影响所示出的半字节的数量(例如,将示出4个半字节)。
TEST_ASSERT_EQUAL(expected, actual)
另一种TEST_ASSERT_EQUAL_INT的方式
TEST_ASSERT_INT_WITHIN(delta, expected, actual)
断言实际值在预期值的正负增量范围内。 这也具有特定尺寸的变体。
TEST_ASSERT_GREATER_THAN(threshold, actual)
断言实际值大于阈值。 这也具有特定尺寸的变体。
TEST_ASSERT_LESS_THAN(threshold, actual)
断言实际值小于阈值。 这也具有特定尺寸的变体。

数 组

_ARRAY
您可以附加到这些宏中的任何一个,以进行该类型的数组比较。在这里,您需要更加关心被检查值的实际大小。您还将指定一个额外的参数,即要比较的元素数。例如:_ARRAY
TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, elements)
_EACH_EQUAL
另一个数组比较选项是检查数组的每个元素是否等于单个期望值。 为此,请指定 EACH_EQUAL 宏。 例如:
TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, elements)

数值断言:按位

TEST_ASSERT_BITS(mask, expected, actual)
使用整数掩码指定应在另外两个整数之间比较哪些位。 比较掩码中的高位,忽略低位。

TEST_ASSERT_BITS_HIGH(mask, actual)
使用整数掩码指定应检查哪些位以确定它们是否全部设置为高电平。 比较掩码中的高位,忽略低位。

TEST_ASSERT_BITS_LOW(mask, actual)
使用整数掩码指定应检查哪些位以确定它们是否全部设置为低电平。 比较掩码中的高位,忽略低位。

TEST_ASSERT_BIT_HIGH(bit, actual)
测试单个位并验证它是否为高电平。 对于 32 位整数,该位指定为 0-31。

TEST_ASSERT_BIT_LOW(bit, actual)
测试单个位并验证它是否为低电平。 对于 32 位整数,该位指定为 0-31。

数值断言:浮点数

TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual)
TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual)
断言实际值在预期值的正负增量范围内。

TEST_ASSERT_FLOAT_NOT_WITHIN(delta, expected, actual)
TEST_ASSERT_DOUBLE_NOT_WITHIN(delta, expected, actual)
断言实际值不在预期值的正负增量范围内。

TEST_ASSERT_EQUAL_FLOAT(expected, actual)
TEST_ASSERT_EQUAL_DOUBLE(expected, actual)
断言两个浮点值在预期值的一小部分 % 增量内“相等”。

TEST_ASSERT_NOT_EQUAL_FLOAT(expected, actual)
TEST_ASSERT_NOT_EQUAL_DOUBLE(expected, actual)
断言两个浮点值在期望值的一小部分 % 增量内不“相等”。

TEST_ASSERT_LESS_THAN_FLOAT(threshold, actual)
TEST_ASSERT_LESS_THAN_DOUBLE(threshold, actual)
TEST_ASSERT_GREATER_THAN_FLOAT(threshold, actual)
TEST_ASSERT_GREATER_THAN_DOUBLE(threshold, actual)
断言实际值小于或大于阈值。

也有和变化。 它们遵循与以下相同的平等规则: 如果这两个值在预期值的一小部分 % 增量范围内,则断言将通过。LESS_OR_EQUALGREATER_OR_EQUALTEST_ASSERT_EQUAL_FLOATTEST_ASSERT_EQUAL_DOUBLE

字符串断言

TEST_ASSERT_EQUAL_STRING(expected, actual)
比较两个以 null 结尾的字符串。 如果任何字符不同或长度不同,则失败。

TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len)
比较两个字符串。 如果任何字符不同,则失败,在 len 字符之后停止比较。

TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, actual, message)
比较两个以 null 结尾的字符串。 如果任何字符不同或长度不同,则失败。 失败时输出自定义消息。

TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(expected, actual, len, message)
比较两个字符串。 如果任何字符不同,则失败,在 len 字符之后停止比较。 失败时输出自定义消息。

指针断言

大多数指针操作只需使用上面的整数比较即可执行。 但是,为了清楚起见,添加了几个特殊情况。

TEST_ASSERT_NULL(pointer)
如果指针不等于 NULL 则失败

TEST_ASSERT_NOT_NULL(pointer)
如果指针等于 NULL 则失败

内存断言

TEST_ASSERT_EQUAL_MEMORY(expected, actual, len)
比较两个内存块。 对于不能被强制执行为标准类型的类型来说,这是一个很好的通用断言...... 但是,由于它是内存比较,因此您必须小心数据类型被封装。

我们在工程Unity文件夹中创建test_unity.c文件,用于填写unity测试程序,我们先定义测试函数

  1. /*
  2. * test_unity.c
  3. *
  4. * Created on: Feb 14, 2024
  5. * Author: Administrator
  6. */
  7. #include "unity.h"
  8. void test_unity(void)
  9. {
  10. }

并且将void test_unity(void)函数添加到main.h中

  1. /* USER CODE BEGIN EFP */
  2. void test_unity(void);
  3. /* USER CODE END EFP */

然后在main.c中调用 test_unity函数。

  1. /* USER CODE BEGIN 2 */
  2. test_unity();
  3. /* USER CODE END 2 */

这时候编译是可以通过的。

我们接下来开始编写测试函数。

  1. /*
  2. * test_unity.c
  3. *
  4. * Created on: Feb 14, 2024
  5. * Author: Administrator
  6. */
  7. #include "unity.h"
  8. #include <stdio.h>
  9. #include <stdbool.h>
  10. bool IsTrue(bool in)
  11. {
  12. return in;
  13. }
  14. void testFunc(void)
  15. {
  16. TEST_ASSERT_TRUE(IsTrue(true));
  17. TEST_ASSERT_TRUE(IsTrue(false));
  18. }
  19. void test_unity(void)
  20. {
  21. printf("\r\n*****hello unity*******\r\n");
  22. RUN_TEST(testFunc);
  23. UNITY_END();
  24. }

这个时候再编译就会报错

因为我们有两个函数未实现,

  1. /*
  2. * test_unity.c
  3. *
  4. * Created on: Feb 14, 2024
  5. * Author: Administrator
  6. */
  7. #include "unity.h"
  8. #include <stdio.h>
  9. #include <stdbool.h>
  10. void setUp(void)
  11. {
  12. }
  13. void tearDown(void)
  14. {
  15. }
  16. bool IsTrue(bool in)
  17. {
  18. return in;
  19. }
  20. void testFunc(void)
  21. {
  22. TEST_ASSERT_TRUE(IsTrue(true));
  23. TEST_ASSERT_TRUE(IsTrue(false));
  24. }
  25. void test_unity(void)
  26. {
  27. printf("\r\n*****hello unity*******\r\n");
  28. RUN_TEST(testFunc);
  29. UNITY_END();
  30. }

这时候我们就可以编译过了。

用例的初始化:setUp()
用例的释放:tearDown()
这俩分别是在每个case之前和之后都会运行一次。
setUp() 方法用于用例的初始化,比如在执行测试用例之前进行的变量定义、初始化等。
tearDown() 方法则用于用例的释放,比如测试后的清理工作,比如数据还原、资源释放等。

运行输出如下:

符合预期,我们再写几个,完善一下测试程序。

  1. /*
  2. * test_unity.c
  3. *
  4. * Created on: Feb 14, 2024
  5. * Author: Administrator
  6. */
  7. #include "unity.h"
  8. #include <stdio.h>
  9. #include <stdbool.h>
  10. void setUp(void)
  11. {
  12. }
  13. void tearDown(void)
  14. {
  15. }
  16. bool IsTrue(bool in)
  17. {
  18. return in;
  19. }
  20. void testBoolPassFunc(void)
  21. {
  22. TEST_ASSERT_TRUE(IsTrue(true));
  23. }
  24. void testBoolFailFunc(void)
  25. {
  26. TEST_ASSERT_TRUE(IsTrue(false));
  27. }
  28. void testIntFunc(void)
  29. {
  30. TEST_ASSERT_EQUAL_INT(1,1);
  31. uint8_t v = 10;
  32. TEST_ASSERT_EQUAL_INT(10, v);
  33. }
  34. void testArrayPassFunc(void)
  35. {
  36. uint8_t a[3] = {1,2,3};
  37. uint8_t b[3] = {1,2,3};
  38. TEST_ASSERT_EQUAL_HEX8_ARRAY(a, b, 3);
  39. }
  40. void testArrayFailFunc(void)
  41. {
  42. uint8_t a[3] = {1,2,3};
  43. uint8_t b[3] = {1,2,4};
  44. TEST_ASSERT_EQUAL_HEX8_ARRAY(a, b, 3);
  45. }
  46. void testBitsFunc(void)
  47. {
  48. TEST_ASSERT_BITS(0xF0, 0x35, 0x34);
  49. }
  50. void testFloatPassFunc(void)
  51. {
  52. // 这些断言验证actual参数处于expected参数的+/-delta之间。
  53. TEST_ASSERT_FLOAT_WITHIN(0.4, 1.2, 1.5);
  54. }
  55. void testFloatFailFunc(void)
  56. {
  57. // 这些断言验证actual参数处于expected参数的+/-delta之间。
  58. TEST_ASSERT_FLOAT_WITHIN(0.2, 1.2, 1.5);
  59. }
  60. void testStringFunc(void)
  61. {
  62. char *s = "hello unity";
  63. TEST_ASSERT_EQUAL_STRING(s, "hello unity");
  64. }
  65. void testStringMessageFunc(void)
  66. {
  67. char *s = "hello unity";
  68. TEST_ASSERT_EQUAL_STRING_MESSAGE(s, "hello unity!", "I'm a message!");
  69. }
  70. void test_unity(void)
  71. {
  72. printf("\r\n*****hello unity*******\r\n");
  73. RUN_TEST(testBoolPassFunc);
  74. RUN_TEST(testBoolFailFunc);
  75. RUN_TEST(testIntFunc);
  76. RUN_TEST(testArrayPassFunc);
  77. RUN_TEST(testArrayFailFunc);
  78. RUN_TEST(testBitsFunc);
  79. RUN_TEST(testFloatPassFunc);
  80. RUN_TEST(testFloatFailFunc);
  81. RUN_TEST(testStringFunc);
  82. RUN_TEST(testStringMessageFunc);
  83. UNITY_END();
  84. }

当我们把失败的case都去掉后。


本文转载自: https://blog.csdn.net/andylauren/article/details/136113496
版权归原作者 andylauren 所有, 如有侵权,请联系我们删除。

“Unity(单元测试)在STM32上的移植与应用”的评论:

还没有评论