7

我正在研究一个简化版本如下所示的类:

class Http_server {
public:
    void start(int port)
    {
        start_server();
        std::string content_type = extract_content_type(get_request());
    }

private:
    void start_server()
    {
        ...
    }

    std::string get_request()
    {
        ...
    }

    std::string extract_content_type(const std::string& request) const
    {
        ...
    }
};

现在我想为extract_content_type. 问题是:它是私人的,所以我不能从外面调用它。我可以测试的唯一功能是start,但实际上会启动服务器 ( start_server) 并等待请求 ( get_request)。

我怎么看,我有三个选择:

  1. extract_content_type公开_
  2. 提取extract_content_type到实用程序类或命名空间
  3. Make start_serverand get_requestvirtual 并创建一个覆盖它们的模拟对象

我不想公开任何东西或移动到一个只在单个类中使用过一次的实用程序命名空间,所以最不邪恶的是选项 3。

我在 V8 代码库中至少看到了一个示例: http ://code.google.com/p/v8/source/browse/trunk/test/cctest/test-date.cc

不过,我不确定这是否是个好主意。virtual不是 C++ 中的默认值,原因有两个:

  1. 它会导致性能/内存开销(但在我的情况下可能无关紧要)
  2. 并非每个类都应该用作基类,使其明确也是设计决策

你会怎么做?与无用的虚拟生活在一起?还是根本不测试功能?我不喜欢 TDD,我也不想这样做,但是开发像extract_content_type针对测试这样的功能更容易。

4

6 回答 6

3

我想你可能有另一种选择:

让单元测试类成为你的类的朋友来测试

class Foo {
  public:
#ifdef UNITTEST
    friend class FooTest;
#endif
    ...

  protected:
    ...

  private:
    ...
};

这是参考: http: //praveen.kumar.in/2008/01/02/how-to-unit-test-c-private-and-protected-member-functions/

于 2012-09-27T06:04:55.847 回答
3

答案是你不测试私有函数。理想情况下,您甚至不编写它们,而是通过重构来创建它们(尽管我承认这在实践中非常困难)。

在测试您的公共/受保护功能时,应隐式测试您的私有功能。如果私有函数的功能没有以这种方式完全断言,那么这意味着该函数所做的事情在类之外没有可见的影响

这不仅仅是一个 TDD 问题。由于私有函数是一个实现细节,我通常假设我可以在不破坏任何东西的情况下重构它们。如果有一个函数测试,并且我决定重构它的签名,那将不再适用,这让我非常困惑。

于 2012-09-27T06:05:32.247 回答
1

我可以向您推荐允许您使用私有方法的 API。它被称为 Typemock Isolator++。作为一个例子,我创建了一个测试,改变你的extract_content_type方法行为,调用它(尽管它是私有的),然后断言:

TEST_METHOD(TestExtractContentType)
    {   
        Http_server* server = new Http_server();

        std::string res ("result");
        PRIVATE_WHEN_CALLED(server, extract_content_type, NULL).Return(&res);

        std::string result;
        ISOLATOR_INVOKE_MEMBER(result, server, extract_content_type, NULL);

        PRIVATE_ASSERT_WAS_CALLED(server, extract_content_type);
        Assert::AreEqual(string("result"), result);
    }

无需更改代码。我刚刚为编译器添加了 ISOLATOR_TESTABLE 标记以确保。

ISOLATOR_TESTABLE std::string extract_content_type(const std::string& request) const 

你可以在这里阅读更多。在处理单元测试中的非公共成员时非常方便。

于 2016-03-06T10:42:56.837 回答
0

我同意 Björn 的观点。一个类有或没有什么私有功能取决于这个类,调用者不关心。如果您删除该私有方法会发生什么,即您认为提取内容类型并不难,因此您直接在您的start函数中执行它?好吧,您会破坏测试用例,尽管该类正在按应有的方式工作。私人就是私人!:)

我的建议是你把你extract_content_type的内容处理放在一个实用程序类中,并在你的测试中使用该类。然后不需要存在服务器代码来测试这个类。

于 2012-09-27T06:18:56.900 回答
0

如果 extract_content_type 不需要 Http_server 类中包含的任何信息,则它不必属于该类。真的,看起来您需要一个请求本身的类,它可以返回自己的内容类型。然后可以测试该请求类。

于 2012-09-27T05:58:46.037 回答
0

我可以建议一个不同的选择,但我不确定你是否愿意。

您可以创建一个

#define TESTING_VIRTUAL

virtual根据编译时选项,这将扩展为要么扩展为要么扩展为空。因此,如果您正在编译一个测试,您可以将其设置为替换为virtual,如果它用于生产,它就不是虚拟方法。

如果宏扩展为privatepublic取决于您是否在测试模式下编译,则同样可能。

于 2012-09-27T06:06:36.670 回答