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

有帮助吗?

解决方案

也许(a)您需要大量的数据来处理某些角色,其中测试用例将仅阅读它。这也可能是(类)全局数据,其中const 使用权。

也许(b)您需要大量的数据序列,以使测试案例将读取或修改或破坏它。每个测试案例都需要重新定性,并且没有 -const 使用权。

也许两者都有。无论哪种情况,传统的Googletest实施都将使用 测试治具为了封装数据的获取,将在实施固定装置虚拟的实施中获取。 Setup() 会员功能,并通过固定装置的Getter方法访问它。

以下程序说明了一个固定装置,该固定装置同时提供了可变数据和从文件中获取的全局常数数据。

#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),您还可以考虑获取 全局设置通过子类化成员函数 ::testing::Environment, ,但我没有一般的理由倾向于这样做。

...或者硬编码它?

然后是将测试数据完全放在文件中的问题,还是将其硬代码在测试源中。 此刻感到高兴的读者从此只会感到无聊.

一般而言,这是关于循环判断的问题,我认为使用Googletest的使用并不是实质性的。我认为主要考虑是: 是否希望能够在不重建测试套件的情况下改变测试数据的项目?

假设重建测试套件以改变此项目是不可忽略的成本,您预计该项目的内容将来会在将来独立于相关的测试代码而变化。或者对于正在测试的系统的不同配置而言,它可能与关联的测试代码无关。在这种情况下,最好从可以通过测试套件的运行时参数选择的文件或其他来源获取项目。在 googletest 中,子类化 class ::testing::Environment 是用于参数化测试套件资源的设计设施。

如果 事实上 测试数据项的内容与相关的测试代码松散结合,然后将其硬编码为测试箱最不可能是审慎的选择。(并且与其他类型的运行时配置器相反,测试文件具有与源代码同一系统中的版本控制的有价值属性。)

如果测试数据项的内容 与关联的测试代码牢固地结合在一起,然后我有偏见地将其硬编码而不是从数据文件中提取。只是有偏见,而不是教条主义。也许您的测试套件采用强大的库设施来初始化XML文件的公共API测试数据,这些数据也被挂在测试管理和缺陷管理中,即系统。美好的!

我认为,如果测试数据是主要的测试资源 - 测试套件无法生成的基本资源 - 那么它的内容最好是胜任的维护者可以轻松理解和操纵的文本数据。在这种情况下,我当然会考虑C/C ++共有常数的列表是 文本数据 - 它是源代码。如果测试文件包含二进制或艰巨的机器导向数据,则测试套件最能包含其从可读的主要资源中生产的手段。测试套件有时不可避免地要依靠外部采购的“原型”二进制文件,但它们几乎不可避免地会带来测试工程师的严峻景象,而虫子固定器则在十六进制者的前面变灰色。

鉴于主测试数据应该对维护者易读的原则,我们可以将其视为规范,即主测试数据将是“某种代码”:它将是无逻辑的,但这将是程序员习惯于调查和编辑的文本内容。

想象一下,测试软件需要特定的4096 64位未签名整数(大魔术表),并与关联的测试代码紧密结合。它可以用作测试套件的某些源文件中的巨大向量或数组初始化列表。可以从以CSV格式或CSV螺栓固定的线路上维护的数据文件中提取的测试套件提取。

为了从数据文件中提取并反对硬编码,可以敦促(根据Andrew McDonell的答案),从同一源文件中其他代码的修订版中,这很有价值地实现了对BMT的修订。同样,可能会敦促任何构成巨大字面初始化的源代码往往是无法持续的,因此具有维护责任。

但是,这两个要点都可以通过观察结果来反驳,即BMT的定义声明可以在源文件中全部编码。它可能是测试套件的代码审查策略,该套件是测试数据初始化必须 如此编码 - 也许是在遵守独特命名约定的文件中。可以肯定的是,狂热的策略要比坚持必须从文件中提取所有测试数据初始化器的策略要比坚持所有测试数据初始化的策略更为狂热。如果维护者有义务在任何包含的文件中调查BMT,那么文件扩展名是否将没有区别 .cpp, .dat 管他呢:所有问题都是“代码”的可理解性。

对于硬编码并反对从数据文件中提取,可以敦促从数据文件中提取的源 不应该发生 可能会失败的错误从文件中读取正确的数据。这构成了测试开发的开销,以实现真正的测试失败与未能从文件中获取测试数据的正确区分,并清楚地诊断后者的所有可能原因。

在Googletest和功能相对功能的框架的情况下,这一点可以在一定程度上通过贴在多态性固定基金的基础上来反驳 ::testing::Test::testing::Environment. 。这些促进了测试开发人员在测试案例或测试套件初始化中封装测试资源的获取,以便在运行任何测试案例的组成测试之前,成功或诊断出的失败已成功。RAII 可以在设置失败和实际失败之间保持无问题的区分。

尽管如此,数据文件路由有一个不可还原的文件处理开销 框架的RAII功能无助于减少操作的开销。在我与数据文件交易的大量测试系统的交易中,数据文件 只是 比只有在构建时间必须存在并正确正确的源文件的源文件更容易出现操作不事。数据文件更有可能在运行时出现丢失或放错位置,或包含错误的内容,或者以某种方式被允许受到限制,或者以某种方式出现在错误的修订中。它们在测试系统中的用途不像源文件那样简单或严格控制。 不应该发生的事情 进行测试文件是依靠它们的测试系统操作摩擦的一部分,并且与它们的数字成正比。

自从 源文件 可以卫生地将测试数据初始化用于修订跟踪,硬编码可以等同于从文件中提取,而预处理器将提取作为编译的副产品进行提取。有鉴于此,为什么要使用其他承担额外责任的机器来提取它呢?可能会有一个好的答案,例如带有测试管理,缺陷管理系统的建议的XML接口,但是“它是测试数据,所以不要硬编码它”不是一个好的。

即使测试套件必须支持正在测试的系统的各种配置 测试套件的构建配置, ,您也可以(卫生)硬编码它,并让有条件的编译选择正确的硬编码。

到目前为止,我还没有针对测试数据初始化器的基于文件的隔离来挑战修订跟踪的hygeine参数。我刚刚指出,初始化器所用硬编码的常规源文件可以完成此隔离。而且我不想绊倒该参数,但是我想在狂热的结论中停止它,即原则上应该从专用文件中提取测试数据初始化器 - 无论是源文件还是数据文件。

无需详述反对这一结论的理由。这样,在本地的测试代码 较少的 比普通的披萨程序员可以撰写的撰写和组织套件文件的组织可理解,这些文件的速度比必要或健康的更快。规范上,测试套件的所有主要资源都是“某种代码”。程序员的技能包括将代码分配到具有适当粒度的文件中的技能,以确保适当的修订跟踪hygeine。这不是一个机械过程,而是一种专业知识,可供代码审查涵盖。代码审核可以并且应该确保测试数据初始化(无论已完成),并且与所有其他常规方面一样,设计和精心制作的Vis-Vis修订跟踪。

底线:如果您想对各种这些模拟网络响应的测试套件的相同构建,请从文件中阅读。另一方面,如果它是不变的或与测试套件的构建配置的协变量, 为什么不 硬代码吗?

其他提示

(警告 - 此答案是通用的任何单位测试框架)

我宁愿将测试数据文件作为修订控制系统中的单独对象保留。这提供了以下优点:

  • 您可以编码单元测试以接受任何或多个数据文件以测试各种情况
  • 您可以根据需要跟踪数据的更改

如果您不希望读取单元测试执行,则读取数据文件,可以选择在某些情况下是必要的条件,您可以选择编写一个程序或脚本,该程序或脚本生成初始化夹具设置的矢量的C ++代码。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top