2016-09-09 193 views
1

我必须处理没有测试的遗留应用程序。所以在我开始重构之前,我想确保一切都按照原样进行。AnyString()作为单元测试的参数

现在想象一下以下情况:

public SomeObject doSomething(final OtherObject x, final String something) { 
    if(x == null) throw new RuntimeException("x may not be null!"); 
    ... 
} 

现在我想测试空检查,所以可以肯定它的工作原理,我不失去它,一旦我重构。

所以我做了这个

@Test(expected = RuntimeException.class) 
public void ifOtherObjectIsNullExpectRuntimeException() { 
    myTestObject.doSomething(null, "testString"); 
} 

现在,这个工作当然。

但不是“testString”我想传入一个随机字符串。

所以我试着用:

但是,这是不允许的,因为我得到

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: ......你无法使用验证之外的说法匹配器或stubbing

我明白这个意思,但我想知道我是否仍然可以设法做我想做的事情,而不用参数化我的测试或类似的东西。 我可能会使用的唯一库是Junit,AssertJ,Mockito和Powermock。

任何想法?

回答

1

好吧,就像Mockito试图通过这个例外来告诉你,那不是真的如何使用anyString。这些方法只能用于嘲笑。

那么,为什么不试试用一个实际的随机字符串?我个人最喜欢的场景是:java.util.UUID.randomUUID().toString()。这实际上总是会产生一种从未用于以前测试的全新字符串。

我还想补充一点,如果你正在为你的SomeObject课写作测试,你应该避免嘲笑SomeObject的行为。从你的例子来看,你并不完全是这样做的,但看起来你可能正在走这条路。模拟您正在测试的实现的依赖关系,而不是实现本身!这个非常重要;否则你实际上没有测试任何东西。

+0

这适用于字符串,但例如长裤或其他物体? – Sorona

+0

另外,randomUUID()永远不会返回null afaik,所以这是一个主要缺点。 – Sorona

+0

@Sorona请参阅[java.util.Random](https://docs.oracle.com/javase/7/docs/api/java/util/Random.html)。您可以在那里找到用随机值进行测试所需的一切。另外,如果你想专门测试一个空值,那应该是一个单独的单元测试。单元测试应该便宜且快速;不要害怕在你认为有必要的地方添加新的测试用例! – nasukkin

1

那么我没有太多的mockito知识,但你总是可以创建自己的随机字符串生成器。也许可以工作,你可以修改更多类型的输入

2

测试应该是确定性的。在测试中使用随机值会在调试失败的测试时难以重现行为。我建议你为测试创建一个String常数,例如"abcdefg"

+0

我同意许多测试应该是确定性的,但是存在参数化测试存在的原因。你并不总是想要做TDD,但是BDD也是这样,测试套件会尝试各种可能的(和“不可能的”)值,只是为了看你的测试失败。我个人看到两种观点的好处。如果你只进行确定性测试,就像在“测试程序员所考虑的测试”中那样,即使是最基本的真实世界场景,你也不能总是能够覆盖。 – Sorona

+1

@Sorona参数化测试当然可以是确定性的。 –

+0

他们可以,但是,他们并不总是。那么,实际上,如果你在计算机科学中围绕框架和随机性,他们可能总是“确定性”;)但是,我在这里要做的是确保不管第二个参数如何,它总是会失败。因此,我想提高我的机会,如果某个对象出现,它不会破坏我的实现。测试将基本上全天候运行,所以统计... – Sorona

2

你在这里混淆了概念。

所有这些“嘲笑”像anyString助手()是指配置模拟对象时使用。

但是当你检查你的测试代码:

@Test(expected = RuntimeException.class) 
public void ifOtherObjectIsNullExpectRuntimeException() { 
    myTestObject.doSomething(null, "testString"); 
} 

,你会发现:绝对不会有嘲讽参与本次测试。你只需不能在那个地方使用那些Mokito电话;因为那个地方“没有Mokito”。

而只是为了记录 - 无论如何不需要在这里过高。你的逻辑在这里非常清楚:当第一个参数为空时,你抛出异常。因此,作为第二个参数来说,它根本不重要。所以想想一个小时如何用任何第二个论证来测试null,在我眼里是:浪费你的时间。

最后提示:有的java.lang.Objects 而那类具有零一个很好的检验,所以我的生产代码只是看起来像

public SomeObject doSomething(final OtherObject x, final String something) { 
    Objects.requireNonNull(otherObject, "otherObject must not be null"); 
    Objects.requireNonNull(something, "something must not be null"); 

唯一的区别有:要求...抛出NullPointerExceptions

最后终于:有人建议对每个参数进行最终处理;但我不会那样做。它在所有病例的99%中增加了没有值。这意味着你有更多的代码可以阅读;没有很好的理由。但这是一个风格问题。

编辑关于具有测试,以检查为未来的潜在变化注释:你不应该这样做:

  1. 在一定程度上,你的输入是如何验证是一个实现细节。您不测试实现细节。换句话说:
  2. 你的方法有一个特定的合约(例如,你通过编写一个javadoc来非正式地指定“在空输入时抛出NPE”)。您的测试应该确认恰好为表示当前合同。合约是:如果第一个参数为空则抛出。

也许是另一种观点;因为我仍然认为你在这里浪费你的时间!您应确保所有界面清晰,易于理解且易于使用。他们允许你的代码的用户轻松地做正确的事情;并防止他做错事。这就是你应该关注的 - 整体界面的质量! 因此,不要担心如何为潜在的未来变化编写测试;只要确保你的代码库基本一致。

+0

事情是,我同意你的看法。但是真的发生了,人们改变了代码,以便抛出异常'if(a &&!b)'而不是'if(a)'...我想要一种测试方法来确保只有该检查使用第一个参数。 – Sorona

相关问题