为 Google 测试夹具指定构造函数参数

Posted

技术标签:

【中文标题】为 Google 测试夹具指定构造函数参数【英文标题】:Specify constructor arguments for a Google test Fixture 【发布时间】:2016-11-07 12:31:12 【问题描述】:

通过 Google 测试,我想指定一个用于不同测试用例的测试夹具。 夹具应分配和释放类TheClass及其数据管理类TheClassData的对象,其中数据管理类需要数据文件的名称。 对于不同的测试,文件名应该不同。

我定义了以下 Fixture:

class TheClassTest : public ::testing::Test 
 protected:
  TheClassTest(std::string filename) : datafile(filename) 
  virtual ~TheClassTest() 
  virtual void SetUp() 
    data = new TheClassData(datafile);
    tc = new TheClass(data);
  
  virtual void TearDown() 
    delete tc;
    delete data;
  

  std::string datafile;
  TheClassData* data;
  TheClass* tc;
;

现在,不同的测试应该使用具有不同文件名的夹具。 将其想象为设置测试环境。

问题:如何从测试中指定文件名,即如何调用夹具的非默认构造函数?

我发现了诸如 ::testing::TestWithParam<T>TEST_P 之类的东西,这并没有帮助,因为我不想用不同的值运行一个测试,而是用一个夹具运行不同的测试。

【问题讨论】:

所以你想自己运行那个夹具? google test 默认测试运行器无法实例化带参数的fixture。 我想使用夹具运行测试(可能是TEST_F)。所以答案是,这不可能吗?谢谢。 我认为TestWithParam<T>TEST_P 正是您所需要的。查找Advanced Docs 如何在实践中使用它们。您始终可以在测试用例中实例化被测实例(我假设它是TheClass)。 【参考方案1】:

根据另一位用户的建议,您无法实现您想要的 通过使用非默认构造函数实例化夹具。然而, 还有其他方法。只需重载SetUp 函数并 在测试中显式调用该版本:

class TheClassTest : public ::testing::Test 
protected:
    TheClassTest() 
    virtual ~TheClassTest() 
    void SetUp(const std::string &filename) 
        data = new TheClassData(filename);
        tc = new TheClass(data);
    
    virtual void TearDown() 
        delete tc;
        delete data;
    

    TheClassData* data;
    TheClass* tc;
;

现在在测试中只需使用这个重载来设置文件名:

TEST_F(TheClassTest, MyTestCaseName)

    SetUp("my_filename_for_this_test_case");

    ...

无参数TearDown会自动清理 测试完成。

【讨论】:

太棒了。非常感谢! SetUp 方法仍将被 gtest 调用,导致两次调用 SetUp ...因此在这种情况下存在内存泄漏,因为 TearDown 仅删除最后的分配。我会将 SetUp 重命名为其他名称,以便 gtest 不会调用它。 对不起,我错了。由于您的 Setup 带有一个参数,因此 gtest 不会调用它。我会将 SetUp 重命名为其他名称,因为它可能与 gtest 调用的 SetUp() 混淆。【参考方案2】:

使用当前类作为您的灯具的基类:

class TheClassTestBase : public ::testing::Test 
 protected:
  TheClassTestBase(std::string filename) : datafile(filename) 
  ...
 ;

对于每个特定的文件名 - 使用派生的夹具:

class TheClassTestForFooTxt : public TheClassTestBase 
protected:
    TheClassTestForFooTxt() : TheClassTestBase ("foo.txt") 
;

但是,对于每组参数,这都是额外的步骤 - 因此您可以尝试使用模板或宏来轻松完成它。喜欢:

template <typename ClassTestTag>
struct ClassTestParams

    static std::string filename;
;

template<typename  ClassTestTag>
class TheClassTest : public TheClassTestBase 
protected:
    TheClassTest() : TheClassTestBase (ClassTestParams<ClassTestTag>::filename) 
;

然后 - 对于每组参数 - 这样做:

class FooTxtTag ;
template <> std::string ClassTestParams<FooTxtTag>::value = "foo.txt";
using TheClassTestForFooTxt = TheClassTest<FooTxtTag>;
TEST_F(TheClassTestForFooTxt, xxxx) 

但是 - 在您的具体情况下 - 我也会尝试 GoogleTest:type-parameterized-tests。

【讨论】:

谢谢。我认为这对于我的问题规模来说有点过分了。 文档链接已损坏【参考方案3】:

解决这个问题的另一种好方法是扩展您的夹具,并在扩展类中提供一个新的默认构造函数,该构造函数使用您需要的参数调用旧构造函数。例如:

struct MySpecializedTestFixture : public GenericTestFixture

  MySpecializedTestFixture() : GenericTestFixture("a thing", "another thing") 
;

TEST_F(MySpecializedTestFixture, FancyTest)

  // Use the thing environment and make some assertions.

【讨论】:

【参考方案4】:

如果您按照建议的here 重载SetUp 方法,并且要确保记住使用重载的SetUp,则可以在TearDown 方法中使用断言。

class my_fixture : public ::testing::Test

protected:
    bool SETUP_HIT_FLAG = false;

    void SetUp(double parameter)
    
        ...
        SETUP_HIT_FLAG = true;
    

    void TearDown() override
    
        assert(SETUP_HIT_FLAG && "You forgot to call SetUp with your parameter!");
    
;

【讨论】:

【参考方案5】:

对于这种特殊情况,我觉得完全摆脱测试夹具要容易得多。 SetUp 函数可以替换为使用所需文件名实例化类的辅助函数。这允许使用TEST 而不是TEST_PTEST_F。现在每个测试用例都是一个独立的测试,它使用辅助函数或直接在测试用例的主体中创建自己的测试类实例。

例如:

using namespace testing;
TEST(FooClassTest, testCase1)

    FooClass fooInstance("File_name_for_testCase1.txt");
    /* The test case body*/
    delete fooInstance;

【讨论】:

OP 询问有关使用测试夹具的问题。这个例子没有展示如何重用datafiledatatcdelete fooInstance 怎么了?

以上是关于为 Google 测试夹具指定构造函数参数的主要内容,如果未能解决你的问题,请参考以下文章

Java构造函数

将参数传递给夹具函数

Java反射之剖析构造函数

C++类-使用默认参数的构造函数

C++类-使用默认参数的构造函数

指定要使用的 Unity IoC 容器的构造函数