gpt4 book ai didi

c++ - 单元测试时从哪里加载 stub 数据

转载 作者:行者123 更新时间:2023-12-01 11:39:26 35 4
gpt4 key购买 nike

为了进行单元测试,我需要模拟网络响应。响应通常是一个字节流,存储为const vector<uint8_t>。但是对于单元测试,我想使用在CPP文件中进行硬编码或在同一解决方案中从文件读取的数据生成 vector 。我的示例数据约为6 kb。使用googletest时将数据放在何处的一般指导是什么?

最佳答案

也许(a)您需要大量数据才能完成某些任务,
测试用例将只阅读它。这也可能是(类)全局数据,const访问。

也许(b)您需要大量数据才能完成其中的某个角色
测试用例将读取,修改或销毁它。这需要
根据测试用例重新初始化,并且具有非const访问权限。

也许两者都有。无论哪种情况,传统的googletest实现都会
使用test fixture
封装数据的获取,将在
夹具的虚拟Setup()成员函数的实现,以及
通过夹具的吸气方法访问它。

以下程序说明了一个夹具,该夹具可提供两种情况
从文件获取的可变数据和全局常数数据。

#include <vector>
#include <fstream>
#include <stdexcept>
#include "gtest/gtest.h"

class foo_test : public ::testing::Test
{
protected:
virtual void SetUp() {
std::ifstream in("path/to/case_data");
if (!in) {
throw std::runtime_error("Could not open \"path/to/case_data\" for input");
}
_case_data.assign(
std::istream_iterator<char>(in),std::istream_iterator<char>());
if (_global_data.empty()) {
std::ifstream in("path/to/global_data");
if (!in) {
throw std::runtime_error(
"Could not open \"path/to/global_data\" for input");
}
_global_data.assign(
std::istream_iterator<char>(in),std::istream_iterator<char>());
}
}
// virtual void TearDown() {}
std::vector<char> & case_data() {
return _case_data;
}
static std::vector<char> const & global_data() {
return _global_data;
}

private:
std::vector<char> _case_data;
static std::vector<char> _global_data;

};

std::vector<char> foo_test::_global_data;

TEST_F(foo_test, CaseDataWipe) {
EXPECT_GT(case_data().size(),0);
case_data().resize(0);
EXPECT_EQ(case_data().size(),0);
}

TEST_F(foo_test, CaseDataTrunc) {
EXPECT_GT(case_data().size(),0);
case_data().resize(1);
EXPECT_EQ(case_data().size(),1);
}

TEST_F(foo_test, HaveGlobalData) {
EXPECT_GT(global_data().size(),0);
}


int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

对于情况(a),您还可以考虑在 global SetUp中获取数据
通过子类化 ::testing::Environment来实现成员函数,但是我看不到
倾向于这样做的一般原因。

...还是硬编码?

然后是将测试数据保存在一个文件中还是硬编码的问题
在测试源中。从现在开始,只会对无聊的读者感到无聊。

作为一般问题,这取决于情况的判断,我不认为
googletest的使用可以从根本上扩展规模。我认为主要考虑因素
是:是否希望能够更改一项测试数据而无需
重建测试套件?

如果说重建测试套件以改变该项目是不可忽略的成本,那么您
预计该项目的内容将来会独立变化
相关的测试代码。或者它可以独立于相关的测试代码而变化,
用于被测系统的不同配置。在这种情况下,最好得到
来自文件或其他来源的项目,可由运行时参数选择
测试套件。在googletest中,子类 class ::testing::Environment是一个
设计用于参数化获取测试套件资源的工具。

如果实际上测试数据项的内容与
关联的测试代码,然后将其硬编码到测试用例中的可能性很小
谨慎选择。 (和测试文件相比,其他类型的运行时
配置器具有宝贵的属性,可以在其中进行版本控制
与源代码相同的系统。)

如果测试数据项的内容与
关联的测试代码,那么我偏向于对其进行硬编码而不是提取它
从数据文件。只是有偏见,不是教条式的。也许你的考验
套件采用了强大的库工具,用于初始化来自以下位置的公共(public)API测试数据:
比方说,XML文件也与测试管理和缺陷管理挂钩,
系统。精细!

我绝对希望测试数据文件是主要的
测试资源-测试套件无法生成的资源-然后其内容
最好是能胜任的维护人员容易理解的文本数据
和操纵。在这种情况下,我当然会认为
例如,C / C++十六进制常量列表是文本数据-它是
源代码。如果测试文件包含二进制或令人生畏的面向机器的数据
然后测试套件最好包含清晰可见的生产方式
主要资源。有时不可避免地要依赖测试套件
外部来源的“原型(prototype)”二进制文件,但它们几乎不可避免地需要
测试工程师和错误修复者在十六进制编辑器前变灰的严峻景象。

鉴于基本测试数据应易于维护者理解的原则,我们
可以将主要测试数据视为“某种代码”作为一种规范:它将
没有逻辑,但这将是程序员的文本内容
习惯于测量和编辑。

想象一个特殊的序列,它是4096个64位无符号整数
(大魔术表)是测试软件所必需的,并且紧密相关
嫁接到相关的测试代码。它可能被硬编码为巨大的 vector 或数组
测试套件的某些源文件中的初始化程序列表。它可能是
由测试套件从以CSV格式或以下格式维护的数据文件中提取
CSV标点符号行。

为了从数据文件中提取数据并进行硬编码,可以敦促它
(根据安德鲁·麦克唐纳(Andrew McDonell)的回答),这很有值(value)地实现了对
BMT的修订版,来自同一版本中其他代码的修订版
源文件。同样,可能敦促任何构成框架的源代码
大量的文字初始化往往无法解释,因此需要维护
责任。

但是,这两个观点都可能与以下观点相反:
BMT的声明可能会全部编码在源文件中。它
可能是用于测试数据初始化的测试套件的代码审查策略
必须这样编码-也许在遵循独特命名的文件中
惯例。可以肯定的是狂热的政策,但没有比
它坚持要求所有测试数据初始化程序必须从文件中提取。
如果维护者有义务调查包含BMT的任何文件,
文件扩展名是 .cpp.dat还是
无论如何:所有事情都是“代码”的可理解性。

对于硬编码和防止从数据文件中提取数据,我们建议您使用它
从数据文件中提取必须引入不相关的来源
测试用例中的潜在故障-所有不应发生的错误
可能无法读取文件中的正确数据。这增加了开销
测试开发以对真实测试失败与
无法从文件中获取测试数据,并且无法明确诊断所有可能
后者的原因。

在googletest和功能相当的框架中,这一点可以说是
通过宣传多态夹具基础类在一定程度上克服了
::testing::Test::testing::Environment。这些便利
测试开发人员在测试用例中封装测试资源的获取
或测试套件初始化,以便成功或成功结束
在运行任何测试用例的组成测试之前,如果已诊断出故障,则必须执行此操作。
RAII可以在设置故障和实际故障之间保持毫无问题的划分。

但是,数据文件的文件处理开销不可减少
路线,并且该框架的RAII具有操作开销
什么也没减少。在我与沉重的测试系统交易时
数据文件,数据文件比
源文件,仅在构建时存在且正确。
数据文件在运行时更容易丢失或放错位置,或者
包含格式错误的内容,或者以某种方式被拒绝,或者
以某种方式出现在错误的修订版上。它们在测试系统中的用途
不像源文件那样简单或严格控制。东西
测试数据文件不应该发生的事情是
依赖于它们并与它们的数量成比例的测试系统。

由于源文件可以卫生地封装测试数据初始化
对于修订版本跟踪,对其进行硬编码可以等同于从
文件,由预处理程序作为编译的副产品进行提取。
有鉴于此,为什么要动用其他负有额外责任的机器来提取它呢?
可能会有很好的答案,例如建议的带有测试管理的XML接口(interface),
缺陷管理系统,但是“这是测试数据,所以请不要对其进行硬编码”不是一个好方法。

即使测试套件必须在以下情况下支持系统的各种配置
测试,该调用要求测试数据项的各种实例化(如果有)
此项与测试套件的构建配置有关,您可以
仍然(卫生)对它进行硬编码,然后选择条件编译
正确的硬编码。

到目前为止,我还没有挑战过修订跟踪-hygeine论点
用于基于文件的测试数据初始化程序的分离。我刚刚做了
指出对初始化程序进行硬编码的常规源文件可以
完成隔离。我不想覆盖那个论点,但是
我想停止测试数据初始化程序的疯狂结论
原则上应始终从专用文件中提取-
源文件还是数据文件。

无需担心拒绝该结论的理由。那样
所在的测试代码比一般的披萨吃法难懂
程序员会编写不断增长的测试套件文件并进行组织
令人难以置信的速度远远超过了必要或健康的速度。规范地说,
测试套件的所有主要资源都是“某种代码”。一种
程序员的技能包括将代码分割成文件的技能
具有适当的粒度以确保适当的修订跟踪合辑。
这不是机械过程,而是专业知识,可以覆盖代码审查。
代码审查可以而且应该确保测试数据的初始化,但是
他们完成了,经过精心设计和制作,与修订跟踪相对
就像在所有其他常规方面一样。

底线:如果您希望能够为各种测试版本运行相同版本的测试套件
这些模拟网络响应中,请从文件中读取。另一方面,如果
与测试套件的构建配置不变或协变,为什么不那么困难
编码吗?

关于c++ - 单元测试时从哪里加载 stub 数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23001887/

35 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com