gpt4 book ai didi

c++ - 为了在测试中调用析构函数(用于测试),将基类静态转换为派生类有多可怕?

转载 作者:太空狗 更新时间:2023-10-29 21:32:31 26 4
gpt4 key购买 nike

前言:我知道下面是一个非常肮脏的 hack。我们将重构一些东西(这样我们就可以编写非 hacky 测试,使用 Mocks 甚至可能摆脱 Singletons),但首先我们需要测试覆盖率......

如前所述,我们的项目中有大量的“单例”类型类,即类似的东西。

class SomeSingleton
{
public:
static SomeSingleton& getSingleton()
{
if (m_singletonInstance == nullptr)
m_singletonInstance = new SomeSingleton();
return *m_singletonInstance;
}

protected:
SomeSingleton();
virtual ~SomeSingleton();

static SomeSingleton* m_singletonInstance = nullptr;
}

为了正确运行我们的单元测试,需要“重置”单例(否则我们之前测试的数据可能会保留在单例中,从而影响当前测试)。不幸的是,没有可行的方法来做到这一点(Singleton 实例和析构函数受到保护)。

我们不希望更改我们的生产代码以包含仅测试功能(即不向前声明 ::test::SomeSingletonResetter 作为 SomeSingleton 中的 friend ,不引入公共(public) void resetSingleton_forTestUseOnly() 函数等)——而且我们还希望在测试覆盖到位之前避免对生产代码进行任何重大更改。

所以,这是我们正在考虑的肮脏黑客:在测试项目中,我们从 Singleton 派生了一个非常简单的包装类(即没有成员),带有一个 deletes singleton 的静态函数 - 然而,因为即使从派生类也不能调用析构函数(它是 protected )我们将 static_cast 单例到派生类并删除它:

class SomeSingletonWrapper : public SomeSingleton
{
public:
SomeSingletonWrapper();
~SomeSingletonWrapper() override;

static void reset()
{
//can't delete/call destructor on SomeSingleton since it is protected
delete static_cast<SomeSingletonWrapper*>(m_singletonInstance);
m_singletonInstance = nullptr;
}
}

我们的想法是,这些类本质上是相同大小的,派生类的析构函数将调用基类的析构函数,因此该类会被正确销毁。这行得通吗(直到我们可以重构东西)或者它会在我们面前可怕地爆炸吗?是否有更好的(更简单的)方法来归档这个(无需主要修改生产代码)?

编辑:

我知道另一种选择是不调用析构函数(并且只设置 m_singletonInstance = nullptr),但这会更糟,因为我现在在每次测试和那些单例一直在四处漂浮,直到测试应用程序终止,做着天知道什么……brrr……

最佳答案

根据标准 5.3.5.2:

In the first alternative (delete object), the value of the operand of delete may be a null pointer value, a pointer to a non-array object created by a previous new-expression, or a pointer to a subobject (1.8) representing a base class of such an object (Clause 10). If not, the behavior is undefined.

由于您要删除的是父类(super class),而不是子类,并且指针之前不是作为该父类(super class)的实例创建的,因此您处于未定义的领域。所以你可能会很幸运,它有时会在某些带有某些编译器的系统上工作,但不能保证。

你能覆盖 getSingleton() 吗?在给定的代码中,它不是 virtualstatic,但它可能应该是其中之一。如果是前者,则有一个更简单的解决方案(在包装器中覆盖它)。如果不是,您可能会考虑尝试用包装器替换创建的值:

class SomeSingletonWrapper : public SomeSingleton
{
public:
static void injectWrapper()
{
// Should be null or else someone used it before we replaced it
if( m_singletonInstance != nullptr )
{
throw std::exception( "Singleton wrapping failed!" );
}
m_singletonInstance = new SomeSingletonWrapper();
}

static void resetWrapper()
{
auto wrapper = dynamic_cast<SomeSingletonWrapper*>( m_singletonInstance );
if( wrapper == nullptr )
{
throw std::exception( "Singleton wrapping failed in the reset!" );
}

delete wrapper;
m_singletonInstance = nullptr;
}
}

然后在你的测试中,在任何人使用它之前替换它(但要注意 static order initialization fiasco 其他东西可能首先获得旧实例):

class MyTest
{
public:
void testSetup()
{
SomeSingletonWrapper::injectWrapper();
}

void testCleanup()
{
SomeSingletonWrapper::resetWrapper();
}

void testMySingletonUse()
{
auto& single = SomeSingleton::getInstance();
ASSERT( dynamic_cast<SomeSingletonWrapper*>( &single ) ); // Yay! It worked!
// More testy stuff using 'single'
}
}

关于c++ - 为了在测试中调用析构函数(用于测试),将基类静态转换为派生类有多可怕?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54867452/

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