黑盒测试是关于测试公开可见的界面和用户之间的软件契约。为了测试这一点,通常使用工具或单独的测试程序创建一组测试用例,#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 宏通过条件编译排除测试代码的有用描述。