您的位置:

详解criterion:C语言单元测试框架

作为一名C语言开发工程师,我们都知道单元测试对于保证代码质量是至关重要的。而criterion正是一个为C语言开发者所设计的单元测试框架,可以帮助我们更方便地进行单元测试。本文将会从多个方面阐述criterion的使用方法及其优势。

一、快速入门

使用criterion编写单元测试非常简单,只需包含criterion头文件并在main函数中编写测试用例即可。我们以一个简单的例子来说明:

#include <criterion/criterion.h>

int square(int num) {
  return num * num;
}

Test(my_tests, square_of_zero) {
  cr_assert_eq(0, square(0));
}

Test(my_tests, square_of_positive) {
  cr_assert_eq(4, square(2));
  cr_assert_eq(9, square(3));
}

Test(my_tests, square_of_negative) {
  cr_assert_eq(9, square(-3));
}

上述代码将会测试square函数三个不同输入情况下的输出是否符合预期。我们使用了cr_assert_eq断言来判断程序输出是否和预期一致。

二、使用断言

criterion提供的断言非常丰富,可以满足大部分的单元测试需求。以下是一些常用的断言:

  • cr_assert(expr) - 判断表达式expr是否为真
  • cr_assert_eq(val1, val2) - 判断val1和val2是否相等
  • cr_assert_neq(val1, val2) - 判断val1和val2是否不相等
  • cr_assert_lt(val1, val2) - 判断val1是否小于val2
  • cr_assert_leq(val1, val2) - 判断val1是否小于等于val2
  • cr_assert_gt(val1, val2) - 判断val1是否大于val2
  • cr_assert_geq(val1, val2) - 判断val1是否大于等于val2

我们将会在接下来的测试用例中使用这些断言对我们写的函数进行测试。

三、隔离测试

在实际的测试中,我们有时候需要对某个函数进行测试,但是这个函数所依赖的其他函数可能还没有被实现或者还没有测试通过。这个时候,我们可以使用criterion提供的mock功能。以下是一个示例:

#include <criterion/criterion.h>

// 我们要测试的函数
int sum(int a, int b) {
  return a + b + 1; // 为了方便演示,故意写成了a+b+1,实际上应该是a+b
}

// mock依赖的函数
int add_one(int num) {
  return num + 1;
}

Test(my_tests, sum_of_positive) {
  // 设定mock
  cr_mock(add_one, int, (int num), { return num; });

  // 进行测试
  cr_assert_eq(4, sum(1, 2));

  // 取消mock
  cr_unmock(add_one);
}

我们通过设定mock将add_one函数的输出设定为输入值本身,使得测试用例可以顺利运行,而不需要考虑add_one函数的实际实现。

四、集成其他测试框架

criterion还可以集成其他测试框架,如CMocka和Unity。我们只需要引入criterion_adapter头文件并使用宏即可:

#include <criterion/criterion.h>
#include <criterion/criterion_adapter.h>

// 集成CMocka
#define CTEST_SEGFAULT cr_assert_fail("segmentation fault");
#define CTEST_ABORT cr_skip_test("aborted");

void segfault_test(void **state) {
  int *a = NULL;
  *a = 1;
}

Test(my_tests, using_cmocka,
     .init = cr_redirect_stderr,
     .signal = CTEST_SEGFAULT,
     .abort = CTEST_ABORT) {
  const UnitTest tests[] = {
    unit_test(segfault_test),
  };

  return cmocka_run_group_tests(tests, NULL, NULL);
}

// 集成Unity
Test(my_tests, using_unity) {
  cr_run_test(UnityMain(argc, argv, RunAllTests));
}

上述代码分别演示了如何集成CMocka和Unity,并且展示了如何使用criterion提供的宏。我们可以通过串联测试用例和使用.init、.signal、.abort三个参数来达到我们需要的测试效果。

五、自定义输出

有时候我们需要对测试结果进行自定义输出,criterion也提供了这个功能。以下是一个示例:

#include <criterion/criterion.h>

Test(my_tests, custom_output,
     .init = cr_redirect_stdout,
     .fini = cr_assert_stdout_eq_str("Output for my test\n")) {
  printf("Output for my test\n");
}

我们使用cr_redirect_stdout将标准输出重定向到一个缓冲区中,并使用cr_assert_stdout_eq_str断言判断缓冲区中的输出是否和预期一致。

六、总结

本文从快速入门、使用断言、隔离测试、集成其他测试框架、自定义输出等多个方面对criterion进行了详细的阐述。希望本文可以帮助到初学者了解criterion的使用方法,并且能够更好地进行C语言单元测试工作。