SUT - 测试内部行为TDD

时间:2016-01-31 19:55:51

标签: php unit-testing testing tdd bdd

我对测试要求有疑问。

我们举个例子:

Class Article {

    public void deactive() {//some behaviour, which deactives article}

}

根据我的要求,该文章可以是deactived。我将把它作为Article课程的一部分来实现。

测试将调用deactive方法,但现在呢?我需要在测试结束时检查,如果需求得到满足,那么我需要实现方法isDeactived 这是我的问题,如果我真的应该这样做?此方法仅供我的测试用例使用,不在其他地方。所以我使我的类接口复杂化,只是为了看看断言,如果真的是deactived。

实施它的正确方法是什么?

3 个答案:

答案 0 :(得分:1)

通常认为将测试挂钩添加到类中是可以的。拥有一个稍微混乱的界面并了解你的课程比让它变得不可测试更好。但还有其他一些解决方案。

如果您确保您的方法受到保护或打包私密,您可以使用Guava's @VisibleForTesting annotation之类的内容。除Java以外的语言可能还有其他类似的库。

您还可以从Article继承以访问私有字段。

class ArticleTest extends Article {
    @Test
    public void deactiveTest() {
        this.deactive();
        assertTrue(this.isDeactive);    
    }
}

这一切都假设您有一些字段用于标记对象是否处于活动状态。

可能会出现一些副作用,例如调用数据库,以及一些服务,说您要停用该文章。如果是这样,您应该嘲笑您正在使用的协作者来制作副作用并验证您是否正确调用它们。

例如(在java / mockito中类似伪代码):

@Test
public void deactiveTest() {
    Article underTest = new Article(databaseMock, httpMock); //or use dependency injection framework of your choice...
    underTest.deactive();
    verify(databaseMock).calledOnce().withParams(expectedParams);
    verify(httpMock).calledOnce().withParams(expectedParams);
}

最后一种可能性,如果该字段影响某些其他方法或函数的行为,您可以尝试(再次使用伪代码):

article.deactive()
result = article.post() // maybe returns true if active, else false?
assertFalse(result)

这样,您可以测试结果行为,而不仅仅是检查内部状态。

答案 1 :(得分:0)

听起来你正在按照以下方式编写测试:

assertThatCallingDeactiveMarksArticleAsDeactivated

使用isDeactivated方法,此测试变得微不足道,但正如您所说,您的Article类并不包含此方法。所以,问题变成应该它有那个方法。答案实际上取决于Article类对deactive的真正含义。

我希望active Article在某种程度上与deactive Article的行为不同。否则,状态变化似乎没有理由/没有什么可以测试。

举一个实际的例子,从Article类的客户角度来看。 Something 会触发对deactive的调用。它可能就像用户点击用户界面上的Deactivate按钮/链接一样简单,它可以调用Article类。在此调用之后,我希望用户界面能够以某种方式反映Article是否处于非活动状态(例如通过灰显按钮/链接),但是为了实现这一点,UI需要能够读取Article的状态,这会让我们回到问题,它是如何做到的和/或为什么不是当前代码所需的isDeactivated方法?

不了解更多关于deactive方法的实现(它是否只是设置一个标志,还是以可观察的方式调用其他代码)以及状态更改如何影响{{1}的行为而客户很难给出更具体的回应。

答案 2 :(得分:0)

理想情况下,您不希望测试方法或类的内部,因为这会使测试变得脆弱且紧密耦合。如果您重构生产代码,那么您的测试需要进行重构的更高更改,从而有利于测试。您想尝试整体测试类行为(即调用deactivate做什么)

查看Kent Beck的简单设计的4条规则(按优先顺序排列)。

1)所有测试通过

2)表达意图

3)消除重复

4)最少的元素

最后一条规则是系​​统不应该大于它需要的大小,这是你的问题所在。鉴于这是至少重要元素,并且最好1)传递测试和2)表达意图,在我看来简单地添加isActive()方法是可以接受的。

这也使得这个类更有用和通用,就像你停用一些东西一样,能够验证它的状态似乎是合乎逻辑的。

同样如前所述,必须有调用deactivate的效果,这本身应该进行测试,所以尝试测试一下 - 它可能更好地放置集成测试,或者你必须模拟或存根另一个类。