使用不在标头中的函数对 C 进行单元测试

2023-11-24

我开始进行单元测试,但我无法理解某些东西。我的挣扎归结为如何测试仅在 .c 源代码中而不是在 .h 标头中声明的函数。有些函数不需要在实现之外调用,因为它们只与特定文件相关。由于它们对程序的其他部分不可见,这意味着我的单元测试用例文件看不到这些内部函数,因此使我无法测试它们。我已经通过在测试用例文件中使用前向声明来解决这个问题,但这看起来有点混乱,如果我修改函数参数,那么更改会很痛苦。

这些功能是否不应该被单元测试覆盖?我读过,在 OOP 中,您不应该测试私有函数,因为它们是通过公共函数隐式测试的,但是不覆盖这些函数会让人感觉不舒服(其中一些函数可能会变得非常复杂)。


黑盒测试是关于测试公开可见的界面和用户之间的软件契约。为了测试这一点,通常使用工具或单独的测试程序创建一组测试用例,#include是您的头文件 .h,它定义了您的外部接口。听起来你已经有了这个。伟大的!

缺少的是白盒测试的概念。对于许多行业(如电信、铁路、航空航天或任何其他需要高度保证高可用性和质量的行业)来说,这与黑盒测试一样重要。

对于白盒测试,创建一个单独的“私有”接口,仅由您的白盒测试程序使用。请注意,在C您可以为给定的 C 实现文件创建多个头文件。从编译器的角度来看,没有真正强制执行标头的数量或其名称。最好遵守您的项目或团队规定的约定。

对于我们的项目,我们为外部接口创建一个公共标头(带有简单的 .h 后缀),并为私有接口创建私有标头 (_pi.h),旨在供少数需要访问私有接口(例如白盒测试)的人使用、审核数据结构、内部配置和诊断以及调试工具。当然,_pi.h 后缀只是一种约定,但在实践中效果很好。

白盒测试对于测试内部功能和数据结构非常有用,并且可以远远超出黑盒测试的限制。例如,我们使用白盒测试用例来测试内部接口,看看当我们的数据结构损坏时会发生什么,以及测试我们的代码在内部传递意外参数值时的行为等极端情况。

例如,假设我们有一个名为 foo.c 的文件,我们想要对其执行白盒测试。然后我们将创建两个标头:分别为外部和内部用户创建 foo.h 和 foo_pi.h。

文件 foo.h

#ifndef FOO_H
#define FOO_H

typedef int FooType;

// Public header for Foo
void Foo(FooType fooVal);
void Bar(void);

#endif

文件 foo_pi.h

#ifndef FOO_PI_H
#define FOO_PI_H

// PI should also include the public interface
#include "foo.h"

// Private header for Foo
// Called by White Box test tool 
void FooBar_Test1(FooType fooVal);
void Foo_Internal(void);
void Bar_Internal(void);

#endif

文件 foo.c

#include "foo.h"
#include "foo_pi.h"
// Notice you need to include both headers

// Define internal helpers here
static FooType myFooVal = 0; 
void FooBar_Test1(FooType fooVal) {myFooVal = fooVal;}

void Foo_Internal() {Bar_Internal();}
void Bar_Internal(void) {myFooVal++;}      


// Define external interfaces after the helpers
void Foo(FooType fooVal) {myFooVal = fooVal; Foo_Internal();}
void Bar(void)           {Bar_Internal();}

// Main() not typically included 
// if this is just one module of a bigger project!
int main(int argc, char** argv)
{
 Foo(argc);
}

如果您对所有这些感到困惑#ifndef/#define/#endif事情是,这些是CPP宏,并且这种用法在 C 中并未强制执行,但它是一种广泛使用的约定。有关更多详细信息,请参阅https://stackoverflow.com/a/42744341/6693299

虽然我没有在上面的示例中展示它,但 Foobar_test() 例程(以及任何其他内部测试方法)通常会放置在为内部测试功能保留的单独模块中。然后它们可以从最终产品中打包出来。通过一些我不会在这里描述的奇特的 CPP 预处理,您可以有条件地编译出私有标头和测试函数,并使接口对于生产负载来说是安全的(在白盒测试完成之后)。但这可能也是太很详细!

@doug-currie 的帖子提供了有关如何使用 CPP 宏通过条件编译排除测试代码的有用描述。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用不在标头中的函数对 C 进行单元测试 的相关文章

  • C# 中类似图的实现

    所以我有一个对象 我们称之为 Head 它有一个对象列表 C C1 C2 C3 T T1 T2 和 M M1 M2 并且所有这些都是相互关联的 例如 Head gt C1 C2 C3 T1 T2 M1 M2 T1 gt C1 C2 T2 g
  • Winform DatagridView 数字列排序

    我只使用一个简单的 DataGridView 来保存一堆数据 有趣的是 我在特定列中有小数 但是当按小数列排序时 它的排序是错误的 例如 起始顺序可能是 0 56 3 45 500 89 20078 90 1 56 100 29 2 39
  • 如何使用c#从数据桶中获取所有文档?

    如何获取数据桶中的所有文档 我尝试过一个示例 但我只能获得一个特定的文档 这是我的代码 CouchbaseClient oclient oclient new CouchbaseClient vwspace data bucket name
  • 如何从不同的线程访问控件?

    如何从创建控件的线程以外的线程访问控件 避免跨线程错误 这是我的示例代码 private void Form1 Load object sender EventArgs e Thread t new Thread foo t Start p
  • 选择initializer_list迭代器定义

    Why std initializer list
  • 如何在 Visual Basic DLL 和 C++ DLL 之间创建隔离/免注册 COM?

    我必须在 C DLL 中使用 VB COM DLL 我弄清楚了如何从 C DLL 访问 VB COM DLL 并且它可以工作 现在我遇到了一个问题 我必须使用隔离的 COM 免注册 COM 因为我无法在必须使用它的每台 PC 上注册 DLL
  • FluentAssertions ShouldNotThrow 无法识别异步方法/Func

    我正在尝试检查异步方法是否抛出具体异常 为此 我使用 MSTEST 和 FluentAssertions 2 0 1 我已经检查过这个关于 Codeplex 的讨论 http fluentassertions codeplex com wo
  • 原子存储抛出错误

    我最近升级到了 C 11 兼容编译器 并且尝试将一些代码从 boost 更新到 c 11 标准 我在使用atomic store转换一些代码时遇到了问题 这是一些简单的测试代码 似乎会引发编译器错误 int main std shared
  • 查找方法不适用于 EF6.1 模拟

    我已经使用这些 msdn 指南设置了模拟 使用模拟框架进行测试 EF6 及以上 http msdn microsoft com en us data dn314429 var bsAc db BusAcnts FirstOrDefault
  • 使用 OleDbCommand / OleDbDataAdapter 读取 CSV 文件

    我不明白为什么 但是当我使用 OleDbDataAdapter 或 OleDbCommand 读取 CSV 文件时 在这两种情况下 生成的数据结构良好 它识别文件头中的列 但行数据都是空字符串 我之前已经成功进行过多次 CSV 处理 因此我
  • 如果finally 块包含await,为什么*有时*不会在ThreadAbortException 上执行?

    UPDATE 我不认为这个问题是重复的ThreadAbortException最后可以跳过吗 https stackoverflow com questions 18002668 can threadabortexception skip
  • 如何禁用基于 ValidationRule 类的按钮?

    如何禁用基于 ValidationRule 类的 WPF 按钮 下面的代码可以很好地突出显示 TextBox
  • 在 C# 命令行应用程序中包含并执行 EXE

    所以我找到了一个很棒的小 EXE 命令行应用程序 我们将其称为 program exe 它输出一些我想用 C 操作的数据 我想知道是否有一种方法可以将program exe 打包 到我的Visual Studio项目文件中 这样我就可以将编
  • 实体框架读取列但阻止其更新

    给定一个数据库表 其中有一列包含历史数据但不再填充 实体框架中是否有一种方法可以读取该列 但在使用相同的模型对象时防止它被更新 例如我有一个对象 public class MyObject public string CurrentData
  • 基础设施 - 同步和异步接口和实现? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 在实现库 基础设施时 并且该 API 的用户希望同步和异步使用代码 我读到混合同步和异步并不是一个好主意 例如 同步实现包括等待异步实现 显然
  • 为什么 C++ 标准没有将 sizeof(bool) 定义为 1?

    Size of char signed char and unsigned char由 C 标准本身定义为 1 个字节 我想知道为什么它没有定义sizeof bool also C 03 标准 5 3 3 1 说 sizeof char s
  • 使用任务的经典永无止境的线程循环?

    给出了一个非常常见的线程场景 宣言 private Thread thread private bool isRunning false Start thread new Thread gt NeverEndingProc thread S
  • 在 LP2844Z(Zebra 打印机)上的收据中包含 PNG [重复]

    这个问题在这里已经有答案了 我正在致力于创建一个基于 HTML5 画布的签名 绘图框 目前我们在服务器上将画布保存为PNG 但可以轻松地将base64字符串保存在数据库中 现在的问题是我们如何在打印的收据上添加签名 目前我们使用 GF 字段
  • 如何通过API退出Win32应用程序?

    我有一个使用 Win32 API 编写的 C Win32 应用程序 我希望强制它在其中一个函数中退出 有没有类似的东西Exit or Destroy or Abort 类似的东西会终止它吗 哎呀呀呀呀呀呀 不要做任何这些事情 exit 和
  • 如何根据当前日期时间发现财政年度?

    我需要基于当前或今天的日期时间的财政年度 假设我们认为今天的日期是10 April 2011 那么我需要输出为Financial Year 2012在某些情况下 我需要以短格式显示相同的输出FY12 我想以两种方式显示 在我们的要求中 考虑

随机推荐