9

我正在迈出单元测试的第一步,并且遇到了封装问题。我的类有一些不应该对客户端可见的私有成员变量,但是为了让我将对象置于我想要测试它的状态,我需要设置这些私有变量。

假设我有这样的代码:

Class Foo {

public:
  int action() ;  
private:
  int state ;

} ;

int Foo::action()
{
  if(this->state == 1)
    return 1 ;
  else
    return 0 ;
}

所以现在我想测试Foo::action(),但我需要能够设置Foo::state能够在不同场景下检查功能。一种解决方案是测试代码中的邪恶“ define private public”。但是还有什么更优雅的吗?我想强调这Foo::state是一个不应该被客户端访问的变量,所以我不想声明任何公共设置器。

编辑:

我现在认为扩展我想在测试代码中测试的类并在派生类中包含设置器是可行的,前提是我将私有变量更改为受保护的。但这是一个“仅一代”的解决方案,仍然感觉像是一种黑客行为,而不是一种正确的方法。

编辑2:

在阅读了我得到的答案和评论后(特别感谢 Lieven 和 ap。)我相信我现在尝试测试的实际课程(不是我提供的简单示例)做得太多了,我的问题的答案正在发生变化将它的一些逻辑放到另一个类中,供大佬使用。

4

3 回答 3

4

只有两种可能性(除了重构)

  1. 使用公共接口设置状态。
  2. 如果您无法通过公共接口将其设置为,则该状态是多余的。

选项 2 是不言自明的,很可能不适用于您的案例,因此您只能通过类的公共接口设置状态。

正如您已经提到的,这是可能的,但需要大量代码才能达到正确的状态。这本身可能表明您的课程目前正在做很多事情,是时候将您的课程的一部分重构为更小的、可测试的类了。

从不测试私有方法

如果您发现需要测试私有方法,那么您做错了其他事情。可以这么说,存在“上游”问题。由于此过程中先前的一些其他错误,您已经遇到了这个问题。尝试准确地隔离那是什么,然后将其删除,而不是弯曲您的测试以在测试中走上一条痛苦的脆弱之路。

单元测试私有成员

我建议不要对私有方法进行单元测试。由于它们是私有的,它们可以在每次发布之间以任何可以想象(或不可想象?)的方式进行更改。如果给定的私有方法对类的操作非常重要,以至于您认为它值得测试用例,那么可能是时候将其重构为受保护或公共方法了

一个常见的引用是

你不应该碰你的私处

于 2012-12-20T12:04:16.930 回答
1

如果您的测试类名为 MyTestClass,则将 MyTestClass 添加为类 Foo 中的朋友,以便能够访问其私有成员变量。

Class Foo {
public:
    int action();  
private:
    int state;

    friend class MyTestClass;
};
于 2012-12-20T12:44:28.413 回答
1

您应该有一些publicly(或“ protectedly”)可访问机制来更改私有变量的值state。为简单起见,假设它是一个方法Foo::setState(int inState)。在单元测试中使用它来改变状态,从而测试Foo::action()方法。这确保了任何未来的实现更改都不会影响单元测试(除非“API”Foo::setState()更改 - 在这种情况下,当然,您必须更改单元测试)。

如果您没有这样的更改机制state,则意味着最终用户或调用代码也无法更改它,因此,您不需要对其进行测试(也许这会变得state多余,但我不知道)。

如果私有变量通过其他代码“间接”更改,您将不得不在单元测试中执行相同的代码。您始终可以将外部可见的任何方法追溯到外部提供给代码的输入。基本上,关键是在单元测试中,您必须向代码提供与“真实”场景中相同的输入,然后测试它是否按应有的方式响应。

正如下面评论中所讨论的,如果从输入到被测试代码的“路径”太长,则代码/测试可能必须分成更小的模块或中间点。为了详细说明,将代码流视为:

Input -> A -> B -> C -> Output
// A, B, C are intermediate execution points which are not "publicly accessible".

在上述情况下,你所能做的就是

给定Input,检查是否Output正确。

相反,最好将中间A, B,C至少暴露给单元测试,这样您现在可以将测试分解为:

给定Input,检查是否A正确。

给定A,检查是否B正确。

给定B,检查是否C正确。

给定C,检查是否Output正确。

正如您可以想象的那样,如果测试失败,则更容易找出失败的原因,从而修复它。

于 2012-12-20T12:46:27.567 回答