单元测试c++;。如何测试私人成员? 我想为我的C++应用程序做单元测试。

单元测试c++;。如何测试私人成员? 我想为我的C++应用程序做单元测试。,c++,unit-testing,testing,C++,Unit Testing,Testing,测试类的私有成员的正确形式是什么?创建一个朋友类,该类将测试私有成员、使用派生类或其他一些技巧 测试API使用哪种技术?通常,只测试问题评论中讨论的公共接口 但是,有时测试私有或受保护的方法是有帮助的。例如,该实现可能具有一些对用户隐藏的非平凡复杂性,并且可以通过访问非公共成员来更精确地测试这些复杂性。通常,最好找出一种方法来消除这种复杂性,或者找出如何公开相关部分,但并非总是如此 允许单元测试访问非公共成员的一种方法是通过构造。我自己还没有找到黄金解决方案,但是如果您知道测试框架如何命名其方法

测试类的私有成员的正确形式是什么?创建一个朋友类,该类将测试私有成员、使用派生类或其他一些技巧


测试API使用哪种技术?

通常,只测试问题评论中讨论的公共接口

但是,有时测试私有或受保护的方法是有帮助的。例如,该实现可能具有一些对用户隐藏的非平凡复杂性,并且可以通过访问非公共成员来更精确地测试这些复杂性。通常,最好找出一种方法来消除这种复杂性,或者找出如何公开相关部分,但并非总是如此


允许单元测试访问非公共成员的一种方法是通过构造。

我自己还没有找到黄金解决方案,但是如果您知道测试框架如何命名其方法,您可以使用
friend
来测试私有成员。我使用下面的测试来测试GoogleTest的私有成员。虽然这工作得很好,但请注意这是一种黑客行为,我不在生产代码中使用它

在我要测试的代码(stylesheet.h)的标题中,我有:

在测试中,我有:

#include <gtest/gtest.h>

#define TEST_FRIENDS \
    friend class StylesheetTest_ParseSingleClause_Test; \
    friend class StylesheetTest_ParseMultipleClauses_Test;

#include "stylesheet.h"

TEST(StylesheetTest, ParseSingleClause) {
    // can use private members of class Stylesheet here.
}
#包括
#定义测试朋友\
朋友类样式表测试\
好友类样式表测试;
#包括“stylesheet.h”
测试(StylesheetTest,ParseSingleClause){
//可以在这里使用类样式表的私有成员。
}
如果您添加了一个访问私有成员的新测试,您总是会添加一个新行来测试您的朋友。这种技术的好处是,它在被测试的代码中相当不引人注目,因为您只添加了几个#定义,这些定义在不测试时没有任何效果。缺点是测试中有点冗长


现在我来说说你为什么要这么做。当然,在理想情况下,您拥有定义良好的职责的小型类,并且这些类具有易于测试的接口。然而,在实践中,这并不总是容易的。如果您正在编写库,那么什么是
私有的
公共的
取决于您希望库的使用者能够使用什么(您的公共API),而不是取决于是否需要测试。您可以使用不太可能更改的不变量,这些不变量需要进行测试,但API的使用者对此不感兴趣。然后,API的黑盒测试是不够的。此外,如果您遇到bug并编写额外的测试来防止回归,则有必要测试
private
内容。

测试私有成员的愿望是一种设计气味,通常表明有一个类被困在您的类中,难以摆脱。类的所有功能都应该可以通过其公共方法来实现;不能公开访问的功能实际上并不存在

有两种方法可以帮助您认识到您需要测试您的私有方法是否按照tin上的说明执行。朋友班是其中最糟糕的;它们将测试与被测试类的实现联系在一起,这种方式看起来很脆弱。更好的方法是依赖项注入:生成私有方法的依赖项类属性,测试可以提供其模拟版本,以便允许通过公共接口测试私有方法。最好的方法是提取一个类,该类封装了私有方法作为其公共接口的行为,然后像通常一样测试新类


有关更多详细信息,请参阅。

尽管有关于测试私有方法的适当性的评论,但假设您确实需要。。。例如,在将遗留代码重构为更合适的代码之前处理遗留代码时,经常会出现这种情况。以下是我使用的模式:

// In testable.hpp:
#if defined UNIT_TESTING
#   define ACCESSIBLE_FROM_TESTS : public
#   define CONCRETE virtual
#else
#   define ACCESSIBLE_FROM_TESTS
#   define CONCRETE
#endif
然后,在代码中:

#include "testable.hpp"

class MyClass {
...
private ACCESSIBLE_FROM_TESTS:
    int someTestablePrivateMethod(int param);

private:
    // Stuff we don't want the unit tests to see...
    int someNonTestablePrivateMethod();

    class Impl;
    boost::scoped_ptr<Impl> _impl;
}
#包括“testable.hpp”
类MyClass{
...
可从_测试中访问的私人_:
int someTestablePrivateMethod(int参数);
私人:
//我们不希望单元测试看到的东西。。。
int someNonTestablePrivateMethod();
类Impl;
boost::作用域的ptr\u impl;
}

这比定义测试朋友好吗?它似乎没有备选方案那么冗长,并且在标题中清楚地显示了正在发生的事情。这两种解决方案都与安全无关:如果您真的关心方法或成员,那么它们需要隐藏在不透明的实现中,可能还有其他保护。

回答这个问题涉及许多其他主题。除了CleanCode、TDD和其他方面的宗教信仰外:

有几种方法可以访问私有成员。在任何情况下,您都必须否决测试代码!这在解析C++(预处理器和语言本身)的两个级别都是可能的:

向公众定义所有内容

通过使用预处理器,您可以破坏封装

#define private public
#define protected public
#define class struct
缺点是,交付代码的类与测试中的类不同! 第92.13章中的C++标准表示:

具有不同属性的非静态数据成员的分配顺序 未指定访问控制

这意味着编译器有权为测试重新排序成员变量和虚拟函数。如果没有发生缓冲区溢出,您可能很难保证这不会损害您的类,但这意味着您不会在交付时测试相同的代码。这意味着,如果您访问由代码初始化的对象的成员,该对象使用未定义为
public
private
编译,则成员的偏移量可能不同

朋友

此方法需要更改测试类,以便使其与测试类或测试函数成为朋友。一些测试框架,如gtest(
FRIEND_TEST(…);
)有规范
#define private public
#define protected public
#define class struct
class X
{
private:
    friend class Test_X;
};
class X
{
protected:
    int myPrivate;
};

class Test_X: public X
{
    // Now you can access the myPrivate member.
};
class X
{
#ifndef UNITTEST
private:
#endif
};
#define protected public
 #define private   public
    #include <ClassUnderTest.hpp>
 #undef protected
#undef private
Sometimes, it is required to test private methods. Testing can be done by adding FRIEND_TEST to the class.

    // Production code
    // prod.h

    #include "gtest/gtest_prod.h"
    ...   

    class ProdCode 
        {
         private:
          FRIEND_TEST(ProdTest, IsFooReturnZero);
          int Foo(void* x);
        };

    //Test.cpp
    // TestCode
    ...
    TEST(ProdTest, IsFooReturnZero) 
    {
      ProdCode ProdObj;
      EXPECT_EQ(0, ProdObj.Foo(NULL)); //Testing private member function Foo()

    }

Adding more info, since many are not aware of gtest features.
This is from gtest/gtest_prod.h

https://github.com/google/googletest/blob/master/googletest/include/gtest/gtest_prod.h

// Copyright 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

//
// Google C++ Testing and Mocking Framework definitions useful in production code.
// GOOGLETEST_CM0003 DO NOT DELETE

#ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_
#define GTEST_INCLUDE_GTEST_GTEST_PROD_H_

// When you need to test the private or protected members of a class,
// use the FRIEND_TEST macro to declare your tests as friends of the
// class.  For example:
//
// class MyClass {
//  private:
//   void PrivateMethod();
//   FRIEND_TEST(MyClassTest, PrivateMethodWorks);
// };
//
// class MyClassTest : public testing::Test {
//   // ...
// };
//
// TEST_F(MyClassTest, PrivateMethodWorks) {
//   // Can call MyClass::PrivateMethod() here.
// }
//
// Note: The test class must be in the same namespace as the class being tested.
// For example, putting MyClassTest in an anonymous namespace will not work.

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

#endif  // GTEST_INCLUDE_GTEST_GTEST_PROD_H_