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条回答
唯我独甜
2楼-- · 2019-01-23 02:54

In general, I agree with what others have said on here - only the public interface should be unit tested. Nevertheless, I've just had a case where I had to call a protected method first, to prepare for a specific test case. I first tried the #define protected public approach mentioned above; this worked with Linux/gcc, but failed with Windows/VisualStudio. The reason was that changing protected to public also changed the mangled symbol name and thus gave me linker errors: the library provided a protected __declspec(dllexport) void Foo::bar() method, but with the #define in place, my test program expected a public __declspec(dllimport) void Foo::bar() method which gave me an unresolved symbol error.

For this reason I switched to a friend based solution, doing the following in my class header:

// This goes in Foo.h
namespace unit_test {   // Name this anything you like
struct FooTester; // Forward declaration for befriending
}

// Class to be tested
class Foo 
{
  ...
private:
  bool somePrivateMethod(int bar);
  // Unit test access
  friend struct ::unit_test::FooTester;
};

And in my actual test case, I did this:

#include <Foo.h>
#include <boost/test/unit_test.hpp>
namespace unit_test {
// Static wrappers for private/protected methods
struct FooTester
{
  static bool somePrivateMethod(Foo& foo, int bar)
  {
    return foo.somePrivateMethod(bar);
  }
};
}

BOOST_AUTO_TEST_SUITE(FooTest);
BOOST_AUTO_TEST_CASE(TestSomePrivateMethod)
{
  // Just a silly example
  Foo foo;
  BOOST_CHECK_EQUAL(unit_test::FooTester::somePrivateMethod(foo, 42), true);
}
BOOST_AUTO_TEST_SUITE_END();

This works with Linux/gcc as well as Windows/VisualStudio.

查看更多
欢心
3楼-- · 2019-01-23 02:54

Unit test VariableImpl such that if its behavior is ensured, so is Variable.

Testing internals isn't the worst thing in the world, but the goal is that they can be anything as long as the interfaces contracts are ensured. If that means creating a bunch of weird mock implementations to test Variable, then that is reasonable.

If that seems like a lot, consider that implementation inheritance doesn't create great separation of concerns. If it is hard to unit test, then that is a pretty obvious code smell for me.

查看更多
登录 后发表回答