简体   繁体   English

对于单元测试,是否有一种黑客方法来更改 const 变量的值?

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

I have a C++ 11 header which has a const value declared as my_const_value .我有一个 C++ 11 标头,它的 const 值声明为my_const_value And a function called GetValue that runs a complex logic using the const value and returns an expected value.还有一个名为GetValue的函数,它使用 const 值运行复杂的逻辑并返回一个预期值。

I want to unit test GetValue with different values of my_const_value .我想用my_const_value不同值对GetValue进行单元测试。

I know this is not advisable but for writing a unit test for GetValue , I wish to test GetValue with different values of my_const_value .我知道这是不可取的,但为了为GetValue编写单元测试,我希望用my_const_value不同值测试GetValue Is there some hack-ish way in C++ to change the value of a const even if it a const? 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());
}

I know that I could const_cast my_const_value to a non_const variable.我知道我可以const_cast my_const_value到 non_const 变量。 But that wouldn't help here.但这在这里无济于事。 If there is some hack to change the value of my_const_value by using a pointer or something, that would answer my question.如果有一些技巧可以通过使用指针或其他东西来更改my_const_value的值,那将回答我的问题。

No.不。

Changing the value of something that is declared as const invokes undefined beahviour.更改声明为const调用未定义的行为。 For illustration, consider that this code为了说明,请考虑此代码

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

may print anything.可以打印任何东西。 You could see 4 on the console or 42 , but "Hey you broke the rules, const cannot be modified" would be a valid output as well.您可以在控制台上看到442 ,但是"Hey you broke the rules, const cannot be modified"也是有效的输出。 No matter how you modfiy x , the code has undefined behavior.无论您如何修改x ,代码都有未定义的行为。 Compilers are not required to issue an error or warning, the code is simply invalid and compilers are not mandated to do anything meaningful to it.编译器不需要发出错误或警告,代码只是无效的,编译器没有被强制要求做任何对它有意义的事情。

The only situation where you are allowed to remove constness is when the object actually is not const.允许删除常量的唯一情况是对象实际上不是常量。 Sounds weird no?听起来很奇怪不是吗? See this example:看这个例子:

const int x = 42;
int y = 100;

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

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

The solution to your problem is to write testable code.解决您的问题的方法是编写可测试的代码。 For example:例如:

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;
}

If you like to keep the original signature you can also wrap it (as suggested in a comment):如果您想保留原始签名,您也可以将其包装(如评论中所建议):

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

Now you can test GetValue_impl while GetValue uses the constant.现在您可以在GetValue使用常量时测试GetValue_impl However, I really wonder why you want to test a case that cannot happen.但是,我真的很想知道您为什么要测试一个不可能发生的情况。

I know you are looking for a way how to cast the const away, but I probably would go a different way.我知道您正在寻找一种如何将 const 丢弃的方法,但我可能会采用不同的方式。

You say in your comment:你在评论中说:

Well.好。 I have given my reason.我已经给出了我的理由。 I am testing the logic in GetValue .我正在测试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.我已将my_const_value声明为const但是当将来有人更改该值时,可以将其从5更改为其他值。

If a variable is const and particiaptes in a expresion within a function without being passed to it, then those changes normally shouldn't happen at a regular basis, and should not be expected.如果一个变量是const并且在没有传递给它的情况下参与函数内的表达式,那么这些更改通常不应该定期发生,也不应该被预期。 If you consider the myheader::my_const_value a config value, and as of that might change anytime, then it should be passed to the function in which it is used in an expresion.如果您认为myheader::my_const_value是一个配置值,并且可能随时更改,则应将其传递给在表达式中使用它的函数。

So from the perspective of testing I agree with what idclev 463035818 suggest in the answer, to split the function in two parts in one testable part with a prameter and one that uses a constant.因此,从测试的角度来看,我同意idclev 463035818在答案中的建议,将函数分为两部分,一个是带有参数的可测试部分,另一个是使用常量的部分。

One test that tests how the code currently should behave (what constant it should have)一项测试代码当前应该如何表现的测试(它应该有什么常数)

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

And one for the generic test:一个用于通用测试:

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

That way you have the generic test if the caluclation used by GetValue works.这样,如果GetValue使用的计算有效,您就可以进行通用测试。 And one if for the current verion of you code the value of myheader::GetValue() is the expected one.如果对于当前版本的代码, myheader::GetValue()的值是预期的值。

Hi Hope you are doing good.嗨希望你做得很好。

Just try #define const /* myComment */ in above the stubbed function只需在存根函数上方尝试 #define const /* myComment */

You asked for hackish, here it is :你要求hackish,这里是:

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

#include "header.hh"
#undefine const

...tests...


The only issue I see is that 1.all the const modifiers fall out, which may or not be a problem.我看到的唯一问题是 1.所有的 const 修饰符都掉了,这可能是也可能不是问题。 2. it is kind of intrusive, the compiler as others mentioned treats constants in a particular way, so your test is not quite testing the same code that will run in your real use case. 2. 它有点侵入性,其他人提到的编译器以特定方式处理常量,因此您的测试并没有完全测试将在您的实际用例中运行的相同代码。

There is a similar trick that starts with #define private public then including the header, for accessing private fields of another class from a library.有一个类似的技巧,从#define private public开始,然后包含标题,用于从库中访问另一个类的私有字段。 Nice thing is it does not even break when you link on the library.好消息是当您在库上链接时它甚至不会中断。

Note that none of these things are recommended, they are hackish , but leveraging the preprocessor to bias included files is fine.请注意,这些东西都不是推荐的,它们是hackish ,但利用预处理器来偏置包含的文件是好的。 It will clear all const modifiers, and transitively in other included headers so pretty intrusive.它将清除所有const 修饰符,并在其他包含的标题中传递,非常具有侵入性。

Less hackish is to have a macro TEST and put in your header #ifdef TEST /*non const decl*/ #else /*const decl*/ .少黑客是有一个宏TEST并放入您的标题#ifdef TEST /*non const decl*/ #else /*const decl*/ Then you obviously do #define TEST before including the header which is cleaner than redefining keywords.然后你显然在包含比重新定义关键字更清晰的标题之前执行#define TEST

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM