单元测试 C++。如何测试私人会员?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14186245/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Unit testing c++. How to test private members?
提问by Daniel Saad
I would like to make unit tests for my C++ application.
我想为我的 C++ 应用程序进行单元测试。
What is the correct form to test private members of a class? Make a friend class which will test the private members, use a derived class, or some other trick?
测试类的私有成员的正确形式是什么?创建一个朋友类来测试私有成员,使用派生类或其他一些技巧?
Which technique does the testing APIs use?
测试 API 使用哪种技术?
采纳答案by Mr Fooz
Typically, one only tests the public interface as discussed in the question's comments.
通常,人们只测试问题评论中讨论的公共接口。
There are times however when it is helpful to test private or protected methods. For example, the implementation may have some non-trivial complexities that are hidden from users and that can be tested more precisely with access to non-public members. Often it's better to figure out a way to remove that complexity or figure out how to expose the relevant portions publicly, but not always.
然而,有时测试私有或受保护的方法是有帮助的。例如,实现可能具有一些对用户隐藏的非平凡复杂性,并且可以通过访问非公共成员进行更精确的测试。通常最好找出一种方法来消除这种复杂性或弄清楚如何公开公开相关部分,但并非总是如此。
One way to allow unit tests access to non-public members is via the friendconstruct.
允许单元测试访问非公共成员的一种方法是通过友元结构。
回答by Stefan Weiser
Answering this question touches many other topics. Beside any religiosity in CleanCode, TDD and others:
回答这个问题涉及许多其他主题。除了 CleanCode、TDD 和其他方面的任何宗教信仰:
There are several ways to access private members. In any case you have to overrule the tested code! This is possible on both levels of parsing C++ (preprocessor and language itself):
有几种方法可以访问私有成员。在任何情况下,您都必须否决经过测试的代码!这在解析 C++ 的两个级别(预处理器和语言本身)都是可能的:
Define all to public
定义所有公开
By using the preprocessor you are able to break encapsulation.
通过使用预处理器,您可以打破封装。
#define private public
#define protected public
#define class struct
The disadvantage is, that the class of the delivered code is not the same as in the test! The C++ Standard in chapter 9.2.13 says:
缺点是,交付代码的类与测试中的不一样!第 9.2.13 章中的 C++ 标准说:
The order of allocation of non-static data members with different access control is unspecified.
具有不同访问控制的非静态数据成员的分配顺序未指定。
This means, that the compiler has the right to reorder the member variables and virtual functions for the test. You may struggle, that this won't harm your classes if no buffer overflow happens, but it means, that you won't test the same code as you deliver. It means, that if you access members of an object, that was initialized by code, compiled with private
not defined to public
, the offset of your member may differ!
这意味着,编译器有权为测试重新排序成员变量和虚函数。您可能会挣扎,如果没有发生缓冲区溢出,这不会损害您的类,但这意味着您不会在交付时测试相同的代码。这意味着,如果您访问由代码初始化的对象的成员,使用private
未定义为编译public
,则您的成员的偏移量可能会有所不同!
Friends
朋友们
This method needs to change the tested class for befriending it with the test class or the test function. Some testing frameworks like gtest (FRIEND_TEST(..);
) have special functionality to support this way of accessing private things.
此方法需要更改测试类以使其与测试类或测试函数成为朋友。一些测试框架如 gtest( FRIEND_TEST(..);
) 具有特殊的功能来支持这种访问私有事物的方式。
class X
{
private:
friend class Test_X;
};
It opens the class only for the test and does not open up the world, but you have to modify the code that gets delivered. In my opinion this is a bad thing, because a test should never change the tested code. As a further disadvantage it gives other classes of the delivered code the possibility to intrude your class by naming themselves like a test class (this would also harm the ODR rule of the C++ Standard).
它只为测试打开类,不打开世界,但您必须修改交付的代码。在我看来这是一件坏事,因为测试永远不应该改变被测试的代码。作为另一个缺点,它使交付代码的其他类有可能通过将自己命名为测试类来侵入您的类(这也会损害 C++ 标准的 ODR 规则)。
Declaring the private things protected and derive from the class for tests
声明受保护的私有事物并从类派生以进行测试
Not a very elegant way, very intrusive, but works also:
不是一种非常优雅的方式,非常具有侵入性,但也适用:
class X
{
protected:
int myPrivate;
};
class Test_X: public X
{
// Now you can access the myPrivate member.
};
Any other way with macros
使用宏的任何其他方式
Works, but has the same disadvantages on standard conformity like the first way. e.g.:
有效,但与第一种方式一样,在标准一致性方面具有相同的缺点。例如:
class X
{
#ifndef UNITTEST
private:
#endif
};
I think that the last both ways are no alternatives to the first two ways, because they have no advantages over the first ones, but are more intrusive on the tested code. The first way is very risky, so you may use the befriending approach.
我认为后两种方式都不能替代前两种方式,因为它们比第一种方式没有优势,但对测试代码更具侵入性。第一种方式风险很大,所以你可以使用交朋友的方法。
Some words on the never-test-private-things-discussion. One of the upsides of unit testing at all is, that you will reach very early the point, where you have to improve the design of your code. This is also sometimes one of the downsides of unit testing. It makes object orientation sometimes more complicated, than it has to be. Especially if you follow the rule to design classes in the same way the real world objects are.
关于永不测试私人事物讨论的一些话。单元测试的好处之一是,您将很早就到达必须改进代码设计的地步。这有时也是单元测试的缺点之一。它使面向对象有时比它必须的更复杂。特别是如果您遵循规则以与现实世界对象相同的方式设计类。
Then you have to change the code sometimes into something ugly, because the unit testing approach forces you to do so. Working on complex frameworks, that are used to control physical processes, is one example. There you want to map the code on the physical process, because often parts of the process are already very complex. The dependency list on that processes gets sometimes very long. This is one possible moment, where testing private members is getting nice. You have to trade-off with the advantages and disadvantages of each approach.
然后你有时不得不把代码改成难看的东西,因为单元测试方法迫使你这样做。处理用于控制物理过程的复杂框架就是一个例子。您希望将代码映射到物理流程上,因为流程的某些部分通常已经非常复杂。该进程的依赖列表有时会变得很长。这是一个可能的时刻,测试私有成员变得越来越好。您必须权衡每种方法的优点和缺点。
Classes are getting sometimes complex! Then you have to decide to split them or to take them as they are. Sometimes the second decision makes more sense. In the end it is always a question of which goals you want to achieve (e.g. perfect design, quick incorporation times, low development costs...).
类有时变得复杂!然后,您必须决定拆分它们或按原样使用它们。有时,第二个决定更有意义。最后始终是您想要实现哪些目标的问题(例如完美的设计、快速的整合时间、低开发成本......)。
My Opinion
我的看法
My decision process for accessing private members looks like this:
我访问私人成员的决策过程如下所示:
- Do you need to test private members themselves? (Often this reduces the total number of tests needed)
- If yes, do you see any design advantage to refactor the class?
- If no, befriend the test in your class (use this because of the missing alternatives).
- 你需要自己测试私有成员吗?(通常这会减少所需的测试总数)
- 如果是,您是否认为重构类有任何设计优势?
- 如果不是,请与班级中的测试成为朋友(使用它是因为缺少替代品)。
I don't like the befriending approach, because it changes the tested code, but the risk to test something, that may not be the same as delivered (as possible with the first approach), will not justify the cleaner code.
我不喜欢交朋友的方法,因为它改变了被测试的代码,但是测试某些东西的风险,可能与交付的不同(尽可能使用第一种方法),不会证明更干净的代码是正确的。
BTW: Testing only the public interface is also a fluent matter, because in my experience it changes as often as the private implementation does. So you have no advantage to reduce the test on public members.
顺便说一句:仅测试公共接口也是一件流畅的事情,因为根据我的经验,它的变化与私有实现一样频繁。所以你没有优势减少对公共成员的测试。
回答by jdm
I haven't found a golden solution myself, but you can use friend
to test private members, if you know how the testing framework names it's methods. I use the following to test private members with Google test. While this works quite well, note that it's a hack, and I don't use it in production code.
我自己还没有找到黄金解决方案,但是friend
如果您知道测试框架如何命名它的方法,您可以使用它来测试私有成员。我使用以下内容通过 Google 测试来测试私人成员。虽然这很有效,但请注意,这是一个 hack,我不会在生产代码中使用它。
In the header of the code I want to test (stylesheet.h), I have:
在我要测试的代码的标题 (stylesheet.h) 中,我有:
#ifndef TEST_FRIENDS
#define TEST_FRIENDS
#endif
class Stylesheet {
TEST_FRIENDS;
public:
// ...
private:
// ...
};
and in the test I have:
在测试中我有:
#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.
}
You always add a new line to TEST_FRIENDS if you add a new test that accesses private members. The benefits of this technique are that it is fairly unobstrusive in the tested code, as you only add a few #defines, which have no effect when not testing. The downside is that it is a bit verbose in the tests.
如果添加访问私有成员的新测试,则始终向 TEST_FRIENDS 添加新行。这种技术的好处是它在被测试的代码中相当不引人注目,因为你只添加了几个 #defines,在不测试时没有任何影响。缺点是它在测试中有点冗长。
Now one word as to why you would want to do this. Ideally of course, you have small classes with well-defined responsibilities, and the classes have easily testable interfaces. However, in practice that's not always easy. If you are writing a library, what is private
and public
is dictated by what you want the consumer of the library to be able to use (your public API), and not by what's in need of testing or not. You can have invariants that are very unlikely to change, and need to be tested, but are of no interest to the consumer of your API. Then, black-box-testing of the API is not enough. Also if you encounter bugs and write additional tests to prevent regressions, it can be neccessary to test private
stuff.
现在说说你为什么要这样做。理想情况下,当然,您拥有具有明确职责的小类,并且这些类具有易于测试的接口。然而,在实践中这并不总是那么容易。如果你正在写一个库,是什么private
和public
被你想要的库的消费者能够使用(你的公共API)决定的,而不是有什么需要测试与否。您可以拥有不太可能改变的不变量,并且需要进行测试,但您的 API 的使用者并不感兴趣。那么,仅对 API 进行黑盒测试是不够的。此外,如果您遇到错误并编写额外的测试以防止回归,则可能需要测试private
内容。
回答by darch
The desire to test private members is a design smell, generally indicating that there is a class trapped inside your class struggling to get out. All of the functionality of a class should be exercisable through its public methods; functionality that can't be accessed publicly doesn't actually exist.
测试私有成员的愿望是一种设计气味,通常表明有一个类被困在您的类中,正在努力摆脱。一个类的所有功能都应该可以通过它的公共方法来执行;无法公开访问的功能实际上并不存在。
There are a couple of approaches to realizing that you need to test that your private methods do what they say on the tin. Friend classes are the worst of these; they tie the test to the implementation of the class under test in a way that is prima facie fragile. Somewhat better is dependency injection: Making the private methods' dependencies class attributes that the test can supply mocked-up versions of so as to allow the testing of private methods through the public interface. Best is to extract a class that encapsulates the behavior your private methods have as its public interface, and then test the new class as you normally would.
有几种方法可以意识到您需要测试您的私有方法是否按照他们所说的去做。Friend 课程是其中最糟糕的;他们以一种表面上很脆弱的方式将测试与被测类的实现联系起来。更好的是依赖注入:使测试可以提供模拟版本的私有方法的依赖类属性,以便允许通过公共接口测试私有方法。最好是提取一个类,该类封装了您的私有方法作为其公共接口的行为,然后像往常一样测试新类。
For more details, consult Clean Code.
有关更多详细信息,请参阅清洁代码。
回答by mbells
Notwithstanding the comments regarding the appropriateness of testing private methods, suppose you really need to... this is often the case for example when working with legacy code prior to refactoring it into something more appropriate. Here is the pattern i've used:
尽管有关于测试私有方法的适当性的评论,但假设您确实需要......这通常是这种情况,例如在将遗留代码重构为更合适的内容之前使用遗留代码时。这是我使用的模式:
// In testable.hpp:
#if defined UNIT_TESTING
# define ACCESSIBLE_FROM_TESTS : public
# define CONCRETE virtual
#else
# define ACCESSIBLE_FROM_TESTS
# define CONCRETE
#endif
Then, within the code:
然后,在代码中:
#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;
}
Is it better than defining test friends? It seems less verbose than the alternative, and is clear within the header what is happening. Both solutions have nothing to do with security: if you are really concerned about the methods or members, then these need to be hidden inside an opaque implementation possibly with other protections.
它比定义测试朋友更好吗?它似乎没有替代方案那么冗长,并且在标题中很清楚发生了什么。这两种解决方案都与安全无关:如果您真的很关心方法或成员,那么它们需要隐藏在不透明的实现中,可能还有其他保护措施。
回答by Syam Sanal
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_
回答by Langley
There's a simple solution in C++ using #define. Just wrap the include of your "ClassUnderTest" like this:
在 C++ 中有一个使用 #define 的简单解决方案。只需像这样包装“ClassUnderTest”的包含:
#define protected public
#define private public
#include <ClassUnderTest.hpp>
#undef protected
#undef private
[Credit goes to this article and RonFox][1]
[归功于本文和 RonFox][1]
回答by Philip Zhou
I would prefer adding -Dprivate=public option in Makefile of unit-test, avoiding to modify anything in my original projects
我更喜欢在单元测试的 Makefile 中添加 -Dprivate=public 选项,避免修改我原始项目中的任何内容