简体   繁体   中英

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 . And a function called GetValue that runs a complex logic using the const value and returns an expected value.

I want to unit test GetValue with different values of my_const_value .

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 . Is there some hack-ish way in C++ to change the value of a const even if it a 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. 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.

No.

Changing the value of something that is declared as const invokes undefined beahviour. 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. No matter how you modfiy x , the code has undefined behavior. 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. 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.

You say in your comment:

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.

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. 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.

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.

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. And one if for the current verion of you code the value of myheader::GetValue() is the expected one.

Hi Hope you are doing good.

Just try #define const /* myComment */ in above the stubbed function

You asked for hackish, here it is :

// 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. 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.

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. 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. It will clear all const modifiers, and transitively in other included headers so pretty intrusive.

Less hackish is to have a macro TEST and put in your header #ifdef TEST /*non const decl*/ #else /*const decl*/ . Then you obviously do #define TEST before including the header which is cleaner than redefining keywords.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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