C++ capch.hpp单元测试:如何动态创建测试用例?

C++ capch.hpp单元测试:如何动态创建测试用例?,c++,unit-testing,catch-unit-test,C++,Unit Testing,Catch Unit Test,作为测试的一部分,我想检查代码中几个模块的输出。没有设定数量的模块;可随时添加更多模块。但是,测试每个模块的代码是相同的。因此,我认为将测试代码放在for循环中是理想的。事实上,使用catch.hpp,我已经验证了我可以在测试用例中动态创建部分,其中每个部分对应一个模块。我可以通过将部分宏包含在for循环中来实现这一点,例如: #include "catch.hpp" #include <vector> #include <string> #include "myHead

作为测试的一部分,我想检查代码中几个模块的输出。没有设定数量的模块;可随时添加更多模块。但是,测试每个模块的代码是相同的。因此,我认为将测试代码放在
for
循环中是理想的。事实上,使用
catch.hpp
,我已经验证了我可以在测试用例中动态创建部分,其中每个部分对应一个模块。我可以通过将
部分
宏包含在for循环中来实现这一点,例如:

#include "catch.hpp"
#include <vector>
#include <string>
#include "myHeader.h"

TEST_CASE("Module testing", "[module]") {
    myNamespace::myManagerClass manager;
    std::vector<std::string> modList;
    size_t n;

    modList = manager.getModules();
    for (n = 0; n < modList.size(); n++) {
        SECTION(modList[n].c_str()) {
            REQUIRE(/*insert testing code here*/);
        }
    }
}
通过
main()
可以做到这一点,但我看不出确切的方法。(我是否会将我的
TEST\u案例
代码直接放入
main()
?如果我想将我的
TEST\u案例
代码保存在另一个文件中,会不会影响其他更标准的测试案例?)

我也可以使用
CHECK
宏,而不是
REQUIRE
宏,以避免在模块失败时中止测试用例,但我遇到了相反的问题:它会尝试在早期应该失败的模块上继续测试。如果我能把每个模块放在它自己的测试用例中,那应该会给我理想的行为


是否有一种在CATCH中动态创建测试用例的简单方法?如果是的话,你能给我举个例子说明怎么做吗?我阅读了捕获文档并在网上搜索,但我找不到任何关于如何执行此操作的指示。

有一种方法可以实现您所寻找的目标,但我发现您的做法是错误的:-

单元测试旨在测试每个单元,即编写一个组件和一个测试以验证该组件的正确行为。如果以后决定以某种方式更改一个组件,则更新相应的测试

如果将所有组件的所有测试聚合到同一个文件中,那么隔离行为不同的单元将变得更加困难

如果您希望排除组件的测试,因为它在所有组件中基本相同,您可以执行以下操作之一:

1。将常见测试提取到单独的头文件中

您可以#定义要测试的组件的类型名称,然后包含包含所有测试的头文件:

// CommonTests.t.h
#include "catch.hpp"
TEST_CASE("Object Can be instantiated", "[ctor]")
{
   REQUIRE_NOTHROW(COMPONENT component);
}

// SimpleComponent.t.cpp
#define COMPONENT SimpleComponent
#include "CommonTests.t.h"
这很简单,但有一个缺点-当您运行测试运行程序时,您将有重复的测试(按名称),因此您只能运行所有测试,或者按标记运行

您可以通过字符串化组件名称并将其预先/追加到测试用例名称来解决这个问题

**二,。通过参数化组件调用通用测试**

将常用测试放在单独的文件中,并直接调用常用测试方法:

// CommonTests.t.h
void RunCommonTests(ComponentInterface& itf);

// CommonTests.t.cpp
void RunCommonTests(ComponentInterface& itf)
{
  REQUIRE(itf.answerToLifeUniverseAndEverything() == 42);
}

// SimpleComponent.t.cpp
#include "SimpleComponent.h"
#include "CommonTest.t.h"
#include "catch.hpp"

TEST_CASE("SimpleComponent is default-constructible", "[ctor]")
{
   REQUIRE_NOTHROW(SimpleComponent sc);
}

TEST_CASE("SimpleComponent behaves like common components", "[common]")
{
  SimpleComponent sc;
  RunCommonTests(sc);
}

听起来Catch可能正在向基于属性的测试迁移,我希望这将允许一种动态创建测试用例的方法。同时,以下是我最后做的事情

我为单个模块创建了一个
.cpp
文件,其中包含一个
测试用例
,并为模块名称创建了一个全局变量。(是的,我知道全局变量是邪恶的,这就是为什么我要小心并将其作为最后手段):

模块单元测试.cpp

#include "catch.hpp"
#include <string>
#include "myHeader.h"

extern const std::string g_ModuleName;  // global variable: module name

TEST_CASE("Module testing", "[module]") {
    myNamespace::myManagerClass manager;
    myNamespace::myModuleClass *pModule;
    SECTION(g_ModuleName.c_str()) {
        pModule = manager.createModule(g_ModuleName.c_str());
        REQUIRE(pModule != 0);
        /*insert more testing code here*/
    }
}
#define CATCH_CONFIG_RUNNER
#include "catch.hpp"
#include <string>
#include <cstdio>

std::string g_ModuleName;  // global variable: module name

int main(int argc, char* argv[]) {
    // Make sure the user specified a module name.
    if (argc < 2) {
        std::cout << argv[0] << " <module name> <Catch options>" << std::endl;
        return 1;
    }

    size_t n;
    char* catch_argv[argc-1];
    int result;

    // Modify the input arguments for the Catch Session.
    // (Remove the module name, which is only used by this program.)
    catch_argv[0] = argv[0];
    for (n = 2; n < argc; n++) {
        catch_argv[n-1] = argv[n];
    }

    // Set the value of the global variable.
    g_ModuleName = argv[1];

    // Run the test with the modified command line arguments.
    result = Catch::Session().run(argc-1, catch_argv);

    return result;
}
#include <cstdlib>
#include <vector>
#include <string>
#include "myHeader.h"

int main(int argc, char* argv[]) {
    std::string commandStr;
    int result, status = 0;
    myNamespace::myManagerClass manager;
    std::vector<std::string> modList;
    size_t m, n;

    // Scan for modules.
    modList = manager.getModules();

    // Loop through the module list.
    for (n = 0; n < modList.size(); n++) {
        // Build the command line.
        commandStr = "module_test " + modList[n];
        for (m = 1; m < argc; m++) {
            commandStr += " ";
            commandStr += argv[m];
        }

        // Do a system call to the first executable.
        result = system(commandStr.c_str());

        // If a test fails, I keep track of the status but continue
        // looping so all the modules get tested.
        status = status ? status : result;
    }

    return status;
}
然后,我在一个单独的可执行文件中执行循环(不链接到上面代码中的目标文件):

模块测试所有.cpp

#include "catch.hpp"
#include <string>
#include "myHeader.h"

extern const std::string g_ModuleName;  // global variable: module name

TEST_CASE("Module testing", "[module]") {
    myNamespace::myManagerClass manager;
    myNamespace::myModuleClass *pModule;
    SECTION(g_ModuleName.c_str()) {
        pModule = manager.createModule(g_ModuleName.c_str());
        REQUIRE(pModule != 0);
        /*insert more testing code here*/
    }
}
#define CATCH_CONFIG_RUNNER
#include "catch.hpp"
#include <string>
#include <cstdio>

std::string g_ModuleName;  // global variable: module name

int main(int argc, char* argv[]) {
    // Make sure the user specified a module name.
    if (argc < 2) {
        std::cout << argv[0] << " <module name> <Catch options>" << std::endl;
        return 1;
    }

    size_t n;
    char* catch_argv[argc-1];
    int result;

    // Modify the input arguments for the Catch Session.
    // (Remove the module name, which is only used by this program.)
    catch_argv[0] = argv[0];
    for (n = 2; n < argc; n++) {
        catch_argv[n-1] = argv[n];
    }

    // Set the value of the global variable.
    g_ModuleName = argv[1];

    // Run the test with the modified command line arguments.
    result = Catch::Session().run(argc-1, catch_argv);

    return result;
}
#include <cstdlib>
#include <vector>
#include <string>
#include "myHeader.h"

int main(int argc, char* argv[]) {
    std::string commandStr;
    int result, status = 0;
    myNamespace::myManagerClass manager;
    std::vector<std::string> modList;
    size_t m, n;

    // Scan for modules.
    modList = manager.getModules();

    // Loop through the module list.
    for (n = 0; n < modList.size(); n++) {
        // Build the command line.
        commandStr = "module_test " + modList[n];
        for (m = 1; m < argc; m++) {
            commandStr += " ";
            commandStr += argv[m];
        }

        // Do a system call to the first executable.
        result = system(commandStr.c_str());

        // If a test fails, I keep track of the status but continue
        // looping so all the modules get tested.
        status = status ? status : result;
    }

    return status;
}
#包括
#包括
#包括
#包括“myHeader.h”
int main(int argc,char*argv[]){
std::string commandStr;
int结果,状态=0;
myNamespace::myManagerClass manager;
向量modList;
尺寸m,n;
//扫描模块。
modList=manager.getModules();
//循环浏览模块列表。
对于(n=0;n

是的,它很难看,但我已经证实它是有效的。

我正在使用你的第一个建议,这让我大部分时间都能做到这一点。理想情况下,我希望在运行时动态检测模块,并为每个模块生成一个测试用例,但我开始认为使用Catch是不可能的。一些背景:模块从我定义的抽象基类定义派生类。它们产生不同的输出,但检查输出的程序是相同的。其他开发人员添加了他们自己的模块,所以如果他们可以使用我们的测试功能来检查他们自己的模块是否符合要求,那就太好了。也许我做错了,但我看不到更好的方法。谷歌集团对此有更多的讨论: