用GoogleTest测试私有方法的最佳方法是什么? [英] What is the best way of testing private methods with GoogleTest?

查看:112
本文介绍了用GoogleTest测试私有方法的最佳方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用GoogleTest测试一些私有方法。

  class Foo 
{
私人:
int bar(...)
}

GoogleTest提供了几种方法。



选项1



使用 FRIEND_TEST

  Foo 
{
私人:
FRIEND_TEST(Foo,barReturnsZero);
int bar(...);
}

TEST(Foo,barReturnsZero)
{
Foo foo;
EXPECT_EQ(foo.bar(...),0);
}

这意味着在生产源文件中包含 gtest / gtest.h 。



选项2



声明测试装置作为该类的朋友并在灯具中定义访问器:

  class Foo 
{
个朋友类别FooTest;
私人:
int bar(...);
}

类FooTest:public :: testing :: Test
{
保护:
int bar(...){foo.bar( ...); }
私人:
Foo foo;
}

TEST_F(FooTest,barReturnsZero)
{
EXPECT_EQ(bar(...),0);
}

选项3



Pimpl习惯用语。



有关详细信息:



请注意,此新设计增加了模块化,因此您可能会在系统的其他部分重用这些类(在您不能使用之前,私有方法不可重用)根据定义)。



该测试看起来非常相似,不同之处在于它实际上是这次编译,因为 GetNextToken()方法现在在 Tokenizer 类中公开:

  TEST(Tokenizer,canParseSpaceDelimtedTokens)
{
std :: string input_string = 1 2测试栏;
令牌生成器tokenizer =令牌生成器(input_string);
EXPECT_EQ(tokenizer.GetNextToken(), 1);
EXPECT_EQ(tokenizer.GetNextToken(), 2);
EXPECT_EQ(tokenizer.GetNextToken(),测试);
EXPECT_EQ(tokenizer.GetNextToken(), bar);
EXPECT_EQ(tokenizer.HasMoreTokens(),false);
}

选项5



只是不要测试私有功能。有时它们不值得测试,因为它们将通过公共界面进行测试。很多时候,我看到的测试看起来非常相似,但是测试了两种不同的功能/方法。最终发生的事情是,当需求发生变化(并且总是如此)时,您现在有2个不合格测试,而不是1个。如果您真的测试了所有私有方法,则可能有10个不合格测试而不是1个。 >简而言之,测试私有功能(通过使用 FRIEND_TEST 或将其公开)可以通过公共接口进行测试,这会导致测试重复。您确实不希望这样做,因为没有什么比测试套件使您放慢脚步伤害更大。可以减少开发时间并降低维护成本!如果您测试通过公共接口以其他方式测试的私有方法,则测试套件可能会做相反的事情,从而积极增加维护成本并延长开发时间。当您公开私有功能时,或者使用 FRIEND_TEST 之类的东西时,通常会后悔。



考虑 Tokenizer 类的以下可能实现:





PSS对于软件中的所有内容,答案是这取决于。没有一种适合所有人。解决问题的选项取决于您的特定情况


I would like to test some private methods using GoogleTest.

class Foo
{
private:
    int bar(...)
}

GoogleTest allows a couple of ways of doing this.

OPTION 1

With FRIEND_TEST:

class Foo
{
private:
    FRIEND_TEST(Foo, barReturnsZero);
    int bar(...);
}

TEST(Foo, barReturnsZero)
{
    Foo foo;
    EXPECT_EQ(foo.bar(...), 0);
}

This implies to include "gtest/gtest.h" in the production source file.

OPTION 2

Declare a test fixture as a friend to the class and define accessors in the fixture:

class Foo
{
    friend class FooTest;
private:
    int bar(...);
}

class FooTest : public ::testing::Test
{
protected:
    int bar(...) { foo.bar(...); }
private:
    Foo foo;
}

TEST_F(FooTest, barReturnsZero)
{
    EXPECT_EQ(bar(...), 0);
}

OPTION 3

The Pimpl idiom.

For details: Google Test: Advanced guide.

Are there any other ways to test private methods? What are some pros and cons of each option?

解决方案

There are at least two more options. I'll list out some other options you should consider by explaining a certain situation.

Option 4:

Consider refactoring your code so that the part you want to test is public in another class. Typically when you're tempted to test a class's private method, it's a sign of bad design. One of the most common (anti)paterns that I see is what Michael Feathers calls an "Iceberg" class. "Iceberg" classes have one public method, and the rest are private (which is why it's tempting to test the private methods). It might look something like this:

For example, you might want to test GetNextToken() by calling it on a string successively and seeing that it returns the expected result. A function like this does warrant a test: that behavior isn't trivial, especially if your tokenizing rules are complex. Let's pretend it's not all that complex, and we just want to rope in tokens delimited by space. So you write a test, maybe it looks something like this:

TEST(RuleEvaluator, canParseSpaceDelimtedTokens)
{
    std::string input_string = "1 2 test bar";
    RuleEvaluator re = RuleEvaluator(input_string);
    EXPECT_EQ(re.GetNextToken(), "1");
    EXPECT_EQ(re.GetNextToken(), "2");
    EXPECT_EQ(re.GetNextToken(), "test");
    EXPECT_EQ(re.GetNextToken(), "bar");
    EXPECT_EQ(re.HasMoreTokens(), false);
}

Well, that actually looks pretty nice. We'd want to make sure we maintain this behavior as we make changes. But GetNextToken() is a private function! So we can't test it like this, because it wont even compile. But what about changing the RuleEvaluator class to follow the Single Responsibility Principle (Single Responsibility Principle)? For instance, we seem to have a parser, tokenizer, and evaluator jammed into one class. Wouldn't it be better to just separate those responsibilities? On top of that, if you create a Tokenizer class, then it's public methods would be HasMoreTokens() and GetNextTokens(). The RuleEvaluator class could have a Tokenizer object as a member. Now, we can keep the same test as above, except we are testing the Tokenizer class instead of the RuleEvaluator class.

Here's what it might look like in UML:

Note that this new design increases modularity, so you could potentially re-use these classes in other parts of your system (before you couldn't, private methods aren't reusable by definition). This is main advantage of breaking the RuleEvaluator down, along with increased understandability/locality.

The test would look extremely similar, except it would actually compile this time since the GetNextToken() method is now public on the Tokenizer class:

TEST(Tokenizer, canParseSpaceDelimtedTokens)
{
    std::string input_string = "1 2 test bar";
    Tokenizer tokenizer = Tokenizer(input_string);
    EXPECT_EQ(tokenizer.GetNextToken(), "1");
    EXPECT_EQ(tokenizer.GetNextToken(), "2");
    EXPECT_EQ(tokenizer.GetNextToken(), "test");
    EXPECT_EQ(tokenizer.GetNextToken(), "bar");
    EXPECT_EQ(tokenizer.HasMoreTokens(), false);
}

Option 5

Just don't test the private functions. Sometimes they aren't worth testing because they will be tested through the public interface. A lot of times what I see is tests that look very similar, but test two different functions/methods. What ends up happening is that when requirements change (and they always do), you now have 2 broken tests instead of 1. And if you really tested all your private methods, you might have more like 10 broken tests instead of 1. In short, testing private functions (by using FRIEND_TEST or making them public) that could otherwise be tested through a public interface cause test duplication. You really don't want this, because nothing hurts more than your test suite slowing you down. It's supposed to decrease development time and decrease maintenance costs! If you test private methods that are otherwise tested through a public interface, the test suite may very well do the opposite, and actively increase maintenance costs and increase development time. When you make a private function public, or if you use something like FRIEND_TEST, you'll usually end up regretting it.

Consider the following possible implementation of the Tokenizer class:

Let's say that SplitUpByDelimiter() is responsible for returning a std::vector<std::string> such that each element in the vector is a token. Furthermore, let's just say that GetNextToken() is simply an iterator over this vector. So your tests might look this:

TEST(Tokenizer, canParseSpaceDelimtedTokens)
{
    std::string input_string = "1 2 test bar";
    Tokenizer tokenizer = Tokenizer(input_string);
    EXPECT_EQ(tokenizer.GetNextToken(), "1");
    EXPECT_EQ(tokenizer.GetNextToken(), "2");
    EXPECT_EQ(tokenizer.GetNextToken(), "test");
    EXPECT_EQ(tokenizer.GetNextToken(), "bar");
    EXPECT_EQ(tokenizer.HasMoreTokens(), false);
}

// Pretend we have some class for a FRIEND_TEST
TEST_F(TokenizerTest, canGenerateSpaceDelimtedTokens)
{
    std::string input_string = "1 2 test bar";
    Tokenizer tokenizer = Tokenizer(input_string);
    std::vector<std::string> result = tokenizer.SplitUpByDelimiter(" ");
    EXPECT_EQ(result.size(), 4);
    EXPECT_EQ(result[0], "1");
    EXPECT_EQ(result[1], "2");
    EXPECT_EQ(result[2], "test");
    EXPECT_EQ(result[3], "bar");
}

Well, now let's say the requirements change, and you're now expected to parse by a "," instead of a space. Naturally, you're going to expect one test to break, but the pain increases when you test private functions. IMO, google test should not allow FRIEND_TEST. It is almost never what you want to do. Michael Feathers refers to things like FRIEND_TEST as a "groping tool", since it's trying to touch someone else's private parts.

I recommend avoiding option 1 and 2 when you can, as it typically causes "test duplication", and as a consequence, many more tests than necessary will break when requirements change. Use them as a last resort. Option 1 and 2 are the fastest ways to "test private methods" for here and now (as in the fastest to implement), but they'll really hurt productivity in the long run.

PIMPL can make sense too, but it still allows for some pretty bad design. Be careful with it.

I'd recommend Option 4 (refactoring into smaller testable components) as the right place to start, but sometimes what you really want is Option 5 (testing the private functions through the public interface).

P.S. Here's the relevant lecture about iceberg classes: https://www.youtube.com/watch?v=4cVZvoFGJTU

P.S.S. As for everything in software, the answer is it depends. There is no one size fits all. The option that solves your problem will depend on your specific circumstances.

这篇关于用GoogleTest测试私有方法的最佳方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆