Change value of a public class variable in gtest C++ - c++

ClassToMock.hpp
enum class Flavors {Vanilla, Strawberry};
class ClassToMock
{
public:
Flavors flavorName = Flavors::Vanilla;
.....
}
ClassRunner.cpp
class Runner
{
void main()
{
....
ClassToMock* classToMock = new ClassToMock();
if (classToMock->flavorName == Flavors::Strawberry)
{
...
}
...
}
}
I want to write a unit test that sets the value of class variable flavorName to Flavors::Strawberry and test the if() branch.
If I were to mock functions its easy to mock (mark the function in ClassToMock.hpp as virtual and mock it). But since flavorName is not a function, I am not able to set the value of flavorName to Strawberry in the test code. The value of flavorName is always set to the default value i.e. Vanilla when I call the main() from the TestCode.cpp.
TestCode.cpp
TEST_F(...)
{
Runner* runner = new Runner();
EXPECT_NO_THROW(runner->main());
}
I have also tried to create a mock class,
class MockClassToMock : public ClassToMock
{
public:
Flavors flavorName = Flavors::Vanilla;
}
but still changes are not picked.

Related

How to use mocks with a test fixture in GMock?

I have a test fixture in my tests so I don't have to instantiate objects of my class repeatedly, but I'm not sure how to use mocks with it. To put it simply, this is how the class is defined:
class Class1 {
public:
Class1(std::shared_ptr<Class2> class_two);
void doThisThing() { doThatThing(); }
}
class Class2 {
public:
Class2(Class3* class3_ptr);
int doThatThing();
}
(class 1 is constructed using a shared pointer to class 2. Class 2 is constructed with a pointer to class 3. Class 1 calls on a function "doThisThing" which calls Class 2's function doThatThing.)
I need to create a mock for doThatThing() (and the rest of Class2's functions), but can't figure out how to pass the mock object to Class 1. Here is what I have so far in my testing code:
class TestClass1 : public ::testing::Test {
TestClass1(){
//Construct instance of Class1 and store as member variable
std::shared_ptr<Class3> class_three = std::make_shared<Class3>();
std::shared_ptr<Class2> class_two = std::make_shared<Class2>((Class3*)class_three.get());
class_one = new Class1(class_two);
};
Class1* class_one;
}
MockClass2 : public Class2 {
MOCK_METHOD0(doThatThing, int());
}
TEST_F(TestClass1, doThatThingTest){
MockClass2 mockObj;
**THIS IS WHERE I'M STUCK. How do I get that mockObj into my TestClass1 Fixture? As of now, it is calling the actual function, not the mock***
class_one->doThatThing();
EXPECT_CALL(mockObj, doThatThing());
}
I had to abstract and simplify the actual code, so I hope the above makes sense.
Assuming that your MockClass2 works, you should try something like the following:
Here you should override the functions SetUp that is called right before every call of a test function to prepare your test data. And override TearDown that is called after every call of a test function to clean up test data.
struct TestClass1 : public ::testing::Test
{
void SetUp() override
{
class_two_mock = std::make_shared<MockClass2>();
class_one = std::make_unique<Class1>(class_two_mock);
}
void TearDown() override
{
class_one.reset();
class_two_mock.reset();
}
std::shared_ptr<MockClass2> class_two_mock
std::unique_ptr<Class1> class_one;
};
In the test function you must declare your expectations before something is executed.
TEST_F(TestClass1, doThatThingTest)
{
EXPECT_CALL(*class_two_mock, doThatThing());
class_one->doThatThing();
}
You may need an interface for Class2. The code here is not tested.

gtest setup teardown TestEnvironment - issue with class variable

I have another gtest where I do the following and it works fine:
TEST(TEST1, TestName)
{
ClassName env;
const String original = env(Con::WorkingDir);
Con c = env;
}
However, I want this to be set for another gtest class and hold throughout the entire test fixture. However, I'm getting this error message:
Call of an object of class type without appropriate operator or
conversion functions to pointer-to-function type.
I'm looking at initialize gtest, and I'm not sure what I'm missing for this. It could be use of static variables I'm not familiar with. I don't want ClassName to be static, though.
What am I doing wrong with this?
//this is intended to setup env to use in teardown.
class TestEnvironment : public ::testing::Environment {
public:
static String getEn() {
ClassName env;
static const String sString = env(Con::WorkingDir); //env has the error message here
return sString;
}
}
class UnitTest : public ::testing::Test
{
public:
virtual void SetUp() {
//
}
virtual void TearDown() {
//set env back to initial value
getEn();
//process env info;
}
class UnitTest : public ::testing::Test { //can't use Environment here because of name conflict in our code, although that was used by static const variable setup in example link.
public:
String orig;
}
class UnitTest : public ::testing::Test
{
public:
virtual void SetUp() {
orig = code;
}
virtual void TearDown() {
//process orig;
}
It turned out that even though our code was looking for a const string, we don't have to save it as const.

Specify constructor arguments for a Google test Fixture

With Google test I want to specify a Test fixture for use in different test cases.
The fixture shall allocate and deallocate objects of the class TheClass and its data management class TheClassData, where the data management class requires the name of a datafile.
For the different tests, the file name should vary.
I defined the following 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;
};
Now, different tests should use the fixture with different file names.
Imagine this as setting up a test environment.
The question: How can I specify the filename from a test, i.e. how to call a non-default constructor of a fixture?
I found things like ::testing::TestWithParam<T> and TEST_P, which doesn't help, as I don't want to run one test with different values, but different tests with one fixture.
As suggested by another user, you cannot achieve what you want
by instantiating a fixture using a non-default constructor. However,
there are other ways. Simply overload the SetUp function and
call that version explicitly in the tests:
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;
};
Now in the test simply use this overload to set up filename:
TEST_F(TheClassTest, MyTestCaseName)
{
SetUp("my_filename_for_this_test_case");
...
}
The parameterless TearDown will automatically clean up when
the test is complete.
Use the current class as a base class for your fixtures:
class TheClassTestBase : public ::testing::Test {
protected:
TheClassTestBase(std::string filename) : datafile(filename) {}
...
};
For every specific filename - use derived fixture:
class TheClassTestForFooTxt : public TheClassTestBase {
protected:
TheClassTestForFooTxt() : TheClassTestBase ("foo.txt") {}
};
However this is extra step needed for every set of parameters - so you can try to use templates or macros to get it done with less effort. Like:
template <typename ClassTestTag>
struct ClassTestParams
{
static std::string filename;
};
template<typename ClassTestTag>
class TheClassTest : public TheClassTestBase {
protected:
TheClassTest() : TheClassTestBase (ClassTestParams<ClassTestTag>::filename) {}
};
Then - for every set of parameters - do that:
class FooTxtTag {};
template <> std::string ClassTestParams<FooTxtTag>::value = "foo.txt";
using TheClassTestForFooTxt = TheClassTest<FooTxtTag>;
TEST_F(TheClassTestForFooTxt, xxxx) {}
However - in your specific case - I would also try GoogleTest:type-parameterized-tests.
Another great way to deal with this is to just extend your fixture and in the extended class supply a new default constructor which calls through to the old one with the arguments you require. For example:
struct MySpecializedTestFixture : public GenericTestFixture
{
MySpecializedTestFixture() : GenericTestFixture("a thing", "another thing") {}
};
TEST_F(MySpecializedTestFixture, FancyTest)
{
// Use the thing environment and make some assertions.
}
If you overload the SetUp method as suggested here, and you want to ensure that you remember to use the overloaded SetUp, you can use an assertion in the TearDown method.
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!");
}
};
Another way using templates:
template<int N>
class Fixture : public ::testing::Test { ... }
using FixtureForTest = Fixture<1000>;
TEST_F(FixtureForTest, test) { ... }
For this specific case, I feel it is much easier to get rid of the test fixture altogether. The SetUp function can instead be replaced with a helper function that instantiates the class with the required file name. This permits the use of TEST instead of TEST_P or TEST_F. Now each test case is a standalone test which creates its own test class instances with the helper function or directly in the body of the test case.
For example:
using namespace testing;
TEST(FooClassTest, testCase1)
{
FooClass fooInstance("File_name_for_testCase1.txt");
/* The test case body*/
delete fooInstance;
}

Nesting test runners

I am trying to find out whether and how it is possible to nest JUnit test runners, e.g. combine a GuiceJUnitRunner, a Parameterized and a HierarchicalcontextRunner.
To me, it seems that JUnit was not designed to achieve this easily, otherwise BlockJUnit4ClassRunner should have a method which passes the next Runner as an argument.
Someone also implemented a ParallelParameterized runner, which looks to me like combining Parallel and Parameterized was not easily possible.
When googling for "nested" and "JUnit", it comes up with lots of information for nested classes, but I'm looking for nesting Runners, not classes.
There is something called NestedRunner for running plain old Java classes in nested configuration.
your test starts with #RunWith(NestedRunner.class) and here is the example that I found:
#RunWith(NestedRunner.class)
public class ListTest {
// inner class for sharing common context
public class WithArrayList {
// some context for these tests
ArrayList<String> list = new ArrayList<String>();
public class WhenEmpty {
#Test
public void itIsEmpty() {
assertTrue(list.isEmpty());
}
public class AfterAddingAnElement {
// some more context for these tests
String element = "Element";
// you can use instance initializer to initialize your context
// it will be run once per test
{
// the list is still empty in here
assertTrue(list.isEmpty());
list.add(element);
}
#Test
public void itIsNotEmpty() {
assertFalse(list.isEmpty());
}
#Test
public void itContainsTheElement() {
assertTrue(list.contains(element));
}
#Test
public void addingAnotherElementIncreasesSize() {
int sizeBeforeAdding = list.size();
list.add("AnotherElement");
assertThat(list.size(), is(greaterThan(sizeBeforeAdding)));
}
#Test
public void listSizeIsStillOne() {
assertThat(list.size(), is(equalTo(1)));
}
}
#Test
public void isStillEmpty() {
assertTrue(list.isEmpty());
}
}
public class WithTwoElements {
#Before
public void init() {
list.add("Element1");
list.add("Element2");
}
#Test
public void hasSizeOfTwo() {
assertThat(list.size(), is(equalTo(2)));
}
}
}
}
and here is the source for further info

Why doesn't dynamic partial mocking work for JMockit's #Injectable?

In the following test case where no Expectations have been recorded, I would expect that the dynamic partial mocking feature will be used for the fields A and B which are initialized in UnitToTest using #Injectable. But instead always the method calls are mocked. Only using an invalid filter value for static partial mocking, it is possible to call the real methods:
#Service
class A {
public String doSomething() { return "doSomething"; }
public String doSomethingElse() { return "doSomethingElse"; }
}
#Service
class B {
public String doSomething() { return "doSomething"; }
public String doSomethingElse() { return "doSomethingElse"; }
}
#Service
class UnitToTest {
#Autowired B b;
#Autowired A a;
public B getB() { return b; }
public A getA() { return a; }
}
public class TestClass {
#Tested UnitToTest unit;
// #Mocked({ "someInvalidFilter()" })
#Injectable A a;
// #Mocked({ "someInvalidFilter()" })
#Injectable B b;
#Test
public void test() {
// actual return value is always null if no invalid static partial
// mocking filters are specified above
assertEquals("doSomething", unit.getA().doSomething());
assertEquals("doSomethingElse", unit.getA().doSomethingElse());
assertEquals("doSomething", unit.getB().doSomething());
assertEquals("doSomethingElse", unit.getB().doSomethingElse());
}
}
For me it looks like dynamic partial mocking with JMockit doesn't work for #Injectables. Is that a known restriction?
#Injectables always get injected into #Tested objects, assuming a matching field or constructor parameter can be found; the injection process even takes into consideration DI annotations such as #Inject and #Autowired.
However, an #Injectable instance is always created as an uninitialized (ie, with no state) and fully mocked instance. Partial mocking, on the other hand, is meant for real instances that you instantiate (and initialize) yourself in the test.
So, what you seem to be asking for is that said real instances (partially mocked or not) could be injected into #Tested objects. Indeed, this is not supported (except by calling Deencapsulation.setField), since a motivating use case was never presented by users.
That said, the example test will pass if it is changed to the following:
public class TestClass {
#Tested(fullyInitialized = true) UnitToTest unit;
#Test
public void test() {
assertEquals("doSomething", unit.getA().doSomething());
assertEquals("doSomethingElse", unit.getA().doSomethingElse());
assertEquals("doSomething", unit.getB().doSomething());
assertEquals("doSomethingElse", unit.getB().doSomethingElse());
}
}
The above is an integration test, though, not a unit test.