TDD:您为单元测试公开了哪些方法?

时间:2008-12-22 15:20:17

标签: unit-testing testing tdd

TDD的一个方面我从未完全理解。

假设有人要求您实现一个简单的Stack对象。如果您已经正确完成了设计,那么您将获得一个非常简洁的API。假设:push()pop()isEmpty()。除此之外的任何事情都会过度消耗需求,并让用户有太多空间来捣乱您的代码。

现在让我们假设您要对代码进行单元测试。如果您所有的公共方法都只是上面显示的三个方法,那么您如何去做?到目前为止,这些方法只会进行测试。

因此,您要么添加私有方法,因为它们对您的单元测试用例不可见,所以它们根本无法帮助您。或者你将这些方法公之于众,那就是你努力工作的简约API。现在用户将搞乱你的堆栈,并且肯定会出现错误。

如何处理打开公共测试方法与干净简单API的这种困境?

编辑:只是为了指向正确的方向,获得技术指针(例如“使用此黑客来暴露私有方法”等等)会很好...但我很喜欢对于这两个概念中哪些更为重要,以及如何处理这个主题,我们对更通用的答案更感兴趣。

9 个答案:

答案 0 :(得分:14)

  1. 测试功能;这通常意味着测试公共接口 - 因为不应该通过公共接口访问所有功能吗?如果他们不是,那么他们不是功能!可能有例外,但我想不出任何。

  2. 测试公共接口;任何未直接或间接从公共接口调用的方法都不是 。它们不仅不需要进行测试,而且根本不需要存在。

答案 1 :(得分:6)

您应该看看这个问题: do you test private method?

  

为了不打破封装,我发现私有方法是巨大的,复杂的或重要的,足以需要自己的测试,我只是将它放在另一个类中并在那里公开(Method Object)。然后,我可以轻松地测试现在存在于其自己的类中的先前私有但现在公开的方法。

答案 2 :(得分:4)

使用你的Stack示例,你真的不需要暴露任何内部工作来进行单元测试。重申其他人所说的话,你应该随意拥有所需数量的私有方法,但只能通过你的公共API进行测试。

[Test]
public void new_instance_should_be_empty()
{
  var stack = new Stack();

  Assert.That(stack.IsEmpty(), Is.True);
}

[Test]
public void single_push_makes_IsEmpty_false()
{
  var stack = new Stack();

  stack.Push("first");

  Assert.That(stack.IsEmpty(), Is.False);
}

使用推送和弹出的组合可以模拟Stack类的所有行为,因为这是用户与其进行交互的唯一方式。你不需要任何技巧,因为你应该只测试别人可以使用的东西。

[Test]
public void three_pushes_and_three_pops_results_in_empty_stack()
{
  var stack = new Stack();

  stack.Push("one");
  stack.Push("two");
  stack.Push("three");
  stack.Pop();
  stack.Pop();
  stack.Pop();

  Assert.That(stack.IsEmpty(), Is.True);
}

答案 3 :(得分:2)

有时候我会创建一些方法,这些方法在包级别(Java)或内部(.NET)方法中是私有的,通常使用注释或注释/属性来解释原因。

然而,大部分时间我都避免这样做。公共API不允许您在堆栈案例中测试什么?如果用户可以看到错误并且他们只使用公共API,那么是什么阻止您拨打相同的电话?

我公开其他方式 - 私有方法的时间是,它使得更容易单独测试复杂的一组步骤的一部分 - 如果单个方法调用非常“粗略”,那么测试每个步骤都非常有帮助隔离,即使普通用户不应该看到这些步骤。

答案 4 :(得分:2)

正确的TDD是关于可以通过公共接口测试的测试行为......如果有任何私有方法,那么这些方法应该由任何公共接口间接测试......

答案 5 :(得分:2)

使用TDD,您的所有代码都应该可以从公共接口访问:

  • 首先,您要为自己的功能编写测试。
  • 然后,您为要通过的测试编写最少量的代码。这表明您的功能已实施。

如果没有涵盖某些代码,这意味着此代码无用(删除它并再次运行测试)或者您的测试不完整(某些功能已经实现但未通过测试明确指出)。

答案 6 :(得分:0)

您可以使用Reflection测试私有,但稍后会很痛苦。我认为您需要将测试放在同一个程序集(或程序包)中并尝试使用Internal。这样你就可以得到一些保护,你可以测试你想要测试的东西。

答案 7 :(得分:0)

如果您的私有方法没有通过公共API测试方法间接测试并且需要进行测试,那么我会将您的主类委托给另一个辅助类。

public Stack {
    public ... push(...) {...}
    public ... pop(...) {...}
    public ... isEmpty(...) {...}

    // secondary class
    private StackSupport stackSupport;
    public StackSupport getStackSupport() {...}
    public void setStackSupport(StackSupport stackSupport) {...}
}

public StackSupport {
    public ...yourOldPrivateMethodToTest(...) {...}
}

然后你的私有方法是另一个类中的公共方法。您可以在另一个类测试中测试该公共方法。 : - )

答案 8 :(得分:-1)

使用TDD编码时,我为对象创建公共接口API。在您的示例中,这意味着我的类实现的接口只有push()pop()isEmpty()

然而通过调用这些方法来测试这些方法本身并不是单元测试(本帖末尾更多关于此内容),因为他们最有可能测试co - 操作多个内部方法,共同产生所需的结果,这就是你的问题:这些方法应该是私有的吗?

我的回答是否定的,使用protected(或者用您选择的语言中的等价物)来代替private,这意味着如果您的项目和测试POM结构相似,测试套件类可以在实际类中查看,因为它们实际上位于同一个文件夹中。这些可以被认为是单元测试:你正在测试类本身的功能块,而不是它们的合作。

至于测试各个接口/ API方法,当然也很重要,我也不反对,但这些方法介于unit testingacceptance testing的朦胧线之间。< / p>

在我看来,最重要的是要记住,单元测试会告诉你一个方法是否行为不端,验收测试会判断多个方法/类的合作是否行为错误,并且集成测试会判断多个系统是否合作操作行为不端。