对于单元测试,是否有一种骇人听闻的方法来更改 const 变量的值?

For unit tests, is there a hackish way to change the value of a const variable?

我有一个 C++ 11 头文件,它有一个声明为 my_const_value 的常量值。还有一个名为 GetValue 的函数,它使用 const 值和 returns 预期值运行复杂的逻辑。

我想用 my_const_value 的不同值对 GetValue 进行单元测试。

我知道这是不可取的,但是为了为 GetValue 编写单元测试,我希望用 my_const_value 的不同值测试 GetValue。 C++ 中是否有一些 hack-ish 方法来更改 const 的值,即使它是 const?

//MyHeader.hpp
namespace myheader {

const int my_const_value = 5;

int GetValue() {
    // In real-world, lets say below line of code is a complex logic that needs to be tested by a unit test
    return /my_const_value * 5) / 25;
}

}

#include "MyHeader.hpp"
#include <gtest/gtest.h>

TEST(MyHeaderTest, Testing_Something) {
    EXPECT_EQ(1, myheader::GetValue()); // This is okay

    // I want to test that in the future is the value of my_const_value changes to something else then 
    // myheader::GetValue returns the expected result. But of course, I cannot change my_const_value because it is a const.
    // Is there a way to hack around this for a unit test? Is there a way that I could still hack and change the value of my_const_value?
    myheader::my_const_value = 25;
    EXPECT_EQ(5, myheader::GetValue());
}

我知道我可以 const_cast my_const_value 到 non_const 变量。但这在这里无济于事。如果有一些 hack 可以通过使用指针或其他东西来更改 my_const_value 的值,那将回答我的问题。

没有

更改声明为 const 的内容的值会调用未定义的行为。为了说明,请考虑此代码

const int x = 4;
modify_const_somehow(x,42);   // "magically" assigns 42 to x
std::cout << x;

可以打印任何东西。您可以在控制台上看到 442,但 "Hey you broke the rules, const cannot be modified" 也是有效输出。无论您如何修改 x,代码都有未定义的行为。编译器不需要发出错误或警告,代码只是无效的,编译器不需要对它做任何有意义的事情。

唯一允许删除常量的情况是对象实际上不是常量。听起来很奇怪不是吗?看这个例子:

const int x = 42;
int y = 100;

void foo(const int& a) {
    const_cast<int&>(a) = 4;
}

foo(x);  // undefined behavior !!
foo(y);  // OK !!

您的问题的解决方案是编写可测试的代码。例如:

int GetValue(int value = my_const_value) {
    // In real world, lets say below line of code is a complex logic that needs to be tested by a unit test
    return (value * 5) / 25;
}

如果您想保留原始签名,您也可以将其包装(如评论中所建议):

int GetValue_impl(int value) {
    return (value * 5) / 25;
}
int GetValue() {
    return GetValue_impl(my_const_value);
}

现在您可以测试 GetValue_implGetValue 使用常量。但是,我真的很奇怪你为什么要测试一个不可能发生的案例。

你问的是 hackish,这里是 :

// define the string const to be replaced by empty string
#define const 

#include "header.hh"
#undefine const

...tests...


我看到的唯一问题是 1.all const 修饰符脱落,这可能是问题,也可能不是问题。 2. 它有点侵入性,正如其他人提到的那样,编译器以特定方式处理常量,因此您的测试并没有完全测试将 运行 在您的实际用例中使用的相同代码。

有一个类似的技巧,从 #define private public 开始,然后包括 header,用于从库中访问另一个 class 的私有字段。好消息是当你 link 在图书馆时它甚至不会中断。

请注意 none 这些东西是推荐的,它们是 hackish,但是利用预处理器来偏置包含的文件是可以的。它将清除 all const 修饰符,并在其他包含的 header 中传递,因此非常具有侵入性。

更简单的做法是拥有一个宏 TEST 并放入您的 header #ifdef TEST /*non const decl*/ #else /*const decl*/。那么你显然在包含 header 之前执行 #define TEST 这比重新定义关键字更干净。

我知道您正在寻找一种方法来摆脱 const,但我可能会采用不同的方法。

您在评论中说:

Well. I have given my reason. I am testing the logic in GetValue. I have declared my_const_value as a const but that can be changed from 5 to something else in future when someone changes the value in future.

如果一个变量是 const 并且在没有传递给它的情况下参与函数内的表达式,那么这些更改通常不应该定期发生,也不应该被期望。如果您认为 myheader::my_const_value 是一个配置值,并且可能随时更改,那么它应该传递给在表达式中使用它的函数。

因此,从测试的角度来看,我同意 在答案中提出的建议,将函数分为两部分,一个可测试部分带有参数,另一部分使用常量。

一个测试代码当前应该如何表现的测试(它应该有什么常量)

TEST(MyHeaderTest, Testing_Something1) {
    EXPECT_EQ(5, myheader::my_const_value)
    EXPECT_EQ(1, myheader::GetValue());
    
}

还有一个用于通用测试:

TEST(MyHeaderTest, Testing_Something2) {
    EXPECT_EQ(1, myheader::GetValue_impl(5));
    EXPECT_EQ(5, myheader::GetValue_impl(25));
    // …
}

如果 GetValue 使用的计算有效,那么您就可以进行通用测试。还有一个,如果对于您当前版本的代码,myheader::GetValue() 的值是预期值。

嗨希望你做得很好。

只需在存根函数上方尝试#define const /* myComment */