How to do unit testing on private members (and met

2019-01-23 02:04发布

I am very new to unit testing and I am a little confused.

I am trying to do unit testing (using the Boost unit testing framework) on a C++ class called VariableImpl. Here are the details.

class Variable
{
public:
  void UpdateStatistics (void) {
    // compute mean based on m_val and update m_mean;
    OtherClass::SendData (m_mean);
    m_val.clear ();
  }
  virtual void RecordData (double) = 0;

protected:
  std::vector<double> m_val;

private:
  double m_mean;
};

class VariableImpl : public Variable
{
public:
  virtual void RecordData (double d) {
    // put data in m_val
  }
};

My question is how can I check that the mean is computed correctly? Note that 1) m_mean is protected and 2) UpdateStatistics calls a method of another class and then clears the vector.

The only way I can see would be to add a getter (for instance, GetMean), but I don't like this solution at all, nor I think it is the most elegant.

How should I do?

And what should I do if I were to test a private method instead of a private variable?

TIA,

Jir

8条回答
\"骚年 ilove
2楼-- · 2019-01-23 02:32

Well, unit testing should test units and ideally every class is a self-contained unit – this follows directly from the single responsibility principle.

So testing private members of a class shouldn’t be necessary – the class is a black box that can be covered in a unit test as-is.

On the other hand, this isn’t always true, and sometimes with good reasons (for instance, several methods of the class could rely on a private utility function that should be tested). One very simple, very crufty but ultimately successful solution is to put the following into your unit-test file, before including the header that defines your class:

#define private public

Of course, this destroys encapsulation and is evil. But for testing, it serves the purpose.

查看更多
神经病院院长
3楼-- · 2019-01-23 02:39

Good approach to test the protected data in c + + is the assignment of a friend proxy class:

#define FRIEND_TEST(test_case_name, test_name)\
friend class test_case_name##_##test_name##_Test

class MyClass 
{
private:
  int MyMethod();
  FRIEND_TEST(MyClassTest, MyMethod);
};

class MyClassTest : public testing::Test 
{
public:
  // ...
  void Test1()
  {
    MyClass obj1;
    ASSERT_TRUE(obj1.MyMethod() == 0);
  }

  void Test2()
  {
    ASSERT_TRUE(obj2.MyMethod() == 0);
  }

  MyClass obj2;
};

TEST_F(MyClassTest, PrivateTests) 
{
 Test1();
 Test2(); 
}

see more goolge test (gtest): http://code.google.com/p/googletest-translations/

查看更多
成全新的幸福
4楼-- · 2019-01-23 02:41

While in my opinion the need of testing private members/methods of a class is a code smell, I think that is technically feasible in C++.

As an example, suppose you have a Dog class with private members/methods except for the public constructor:

#include <iostream>
#include <string>

using namespace std;

class Dog {
  public:
    Dog(string name) { this->name = name; };

  private:
    string name;
    string bark() { return name + ": Woof!"; };
    static string Species;
    static int Legs() { return 4; };
};

string Dog::Species = "Canis familiaris";

Now for some reason you would like to test the private ones. You could use privablic to achieve that.

Include a header named privablic.h along with the desired implementation like that:

#include "privablic.h"
#include "dog.hpp"

then map some stubs according to types of any instance member

struct Dog_name { typedef string (Dog::*type); };
template class private_member<Dog_name, &Dog::name>;

...and instance method;

struct Dog_bark { typedef string (Dog::*type)(); };
template class private_method<Dog_bark, &Dog::bark>;

do the same with all static instance members

struct Dog_Species { typedef string *type; };
template class private_member<Dog_Species, &Dog::Species>;

...and static instance methods.

struct Dog_Legs { typedef int (*type)(); };
template class private_method<Dog_Legs, &Dog::Legs>;

Now you can test them all:

#include <assert.h>

int main()
{
    string name = "Fido";
    Dog fido = Dog(name);

    string fido_name = fido.*member<Dog_name>::value;
    assert (fido_name == name);

    string fido_bark = (&fido->*func<Dog_bark>::ptr)();
    string bark = "Fido: Woof!";
    assert( fido_bark == bark);

    string fido_species = *member<Dog_Species>::value;
    string species = "Canis familiaris";
    assert(fido_species == species);

    int fido_legs = (*func<Dog_Legs>::ptr)();
    int legs = 4;
    assert(fido_legs == legs);

    printf("all assertions passed\n");
};

Output:

$ ./main
all assertions passed

You can look at the sources of test_dog.cpp and dog.hpp.

DISCLAIMER: Thanks to insights of other clever people, I have assembled the aforementioned "library" able to access to private members and methods of a given C++ class without altering its definition or behaviour. In order to make it work it's (obviously) required to know and include the implementation of the class.

NOTE: I revised the content of this answer in order to follow directives suggested by reviewers.

查看更多
We Are One
5楼-- · 2019-01-23 02:42

I generally suggest testing the public interface of your classes, not the private/protected implementations. In this case, if it can't be observed from the outside world by a public method, then the unit test may not need to test it.

If the functionality requires a child class, either unit test the real derived class OR create your own test derived class that has an appropriate implementation.

查看更多
戒情不戒烟
6楼-- · 2019-01-23 02:44

For a protected method/variable, inherit a Test class from the class and do your testing.

For a private, introduce a friend class. It isn't the best of solutions but can do the work for you.

Or this hack

#define private public
查看更多
SAY GOODBYE
7楼-- · 2019-01-23 02:51

Example from the google testing framework:

// foo.h
#include "gtest/gtest_prod.h"
class Foo {
  ...
 private:
  FRIEND_TEST(FooTest, BarReturnsZeroOnNull);
  int Bar(void* x);
};

// foo_test.cc
...
TEST(FooTest, BarReturnsZeroOnNull) {
  Foo foo;
  EXPECT_EQ(0, foo.Bar(NULL));
  // Uses Foo's private member Bar().
}

The main idea is the use of the friend cpp keyword. You can extend this example as following:

// foo.h
#ifdef TEST_FOO
#include "gtest/gtest_prod.h"
#endif

class Foo {
  ...
 private:
  #ifdef TEST_FOO
  FRIEND_TEST(FooTest, BarReturnsZeroOnNull);
  #endif
  int Bar(void* x);
};

You can define the TEST_FOO preprocessor in two ways:

1)within the CMakeLists.txt

option(TEST "Run test ?" ON)
if (TEST)
  add_definitions(-DTEST_FOO)
endif()

2)as arguments to your compiler

g++ -D TEST $your_args
查看更多
登录 后发表回答