Mockito风格的anyXXX方法用于单元测试

Nar*_*hai 9 java unit-testing hamcrest mockito

在对某些方法进行单元测试时,可能存在某些情况,其中某些参数的值无关紧要,可以是任何值.

例如,在这段代码中:

public void method(String arg1, String arg2, int arg3){
    if(arg1 == null) throw new NullPointerException("arg1 is null");

    //some other code
}
Run Code Online (Sandbox Code Playgroud)

单元测试的行为,当arg1null然后NPE必须抛出,其他参数的值并不重要,它们可以是任何值或者是null.

所以我想记录这些值对于被测方法无关紧要的事实.

我想到了以下选项:

选项1:定义常量 ANY_XXX

我想明确地创建常数的ANY_STRINGANY_INT,其中包含哪些文件,它可以是任何值,并根据测试方法不关心实际值的固定值.

我可以将所有这些常量放在一个被调用的类中Any,并在所有测试类中重用它们.

选项2:ANY_XXX的随机值

这个选项对我来说似乎有些讨厌,因为我已经在某处读过随机性不应该被带入测试用例.但在这种情况下,这种随机性将不可见,因为参数不会产生任何副作用.

哪种方法更适合更好,可读的测试?

更新:

虽然我可以通过在Any类中定义常量来使用ANY_XXX方法,但我也在考虑使用一些约束生成ANY_XXX值,例如

Any.anyInteger().nonnegative();
Any.anyInteger().negative();

Any.anyString().thatStartsWith("ab");
Run Code Online (Sandbox Code Playgroud)

我想也许Hamcrest Matchers可以用来创建这个链接.但我不确定这种方法是否合适.MockitoanyObject()已经提供了类似的方法,但这些方法仅适用于Mocks和间谍,而不适用于普通物体.我想为普通对象实现相同的功能,以获得更易读的测试.

我为什么要这样做?

假设我有一堂课

class MyObject{

     public MyObject(int param1, Object param2){
          if(param1 < 0) throw new IllegalArgumentException();
          if(param2 == null) throw new NullPointerException();
     }
}
Run Code Online (Sandbox Code Playgroud)

现在在为构造函数编写测试时

class MyObjectTest{

     @Test(expected=NullPointerException.class)
     public void testConstructor_ShouldThrowNullpointer_IfSecondParamIsNull(){

          //emphasizing the fact that value of first parameter has no relationship with result, for better test readability
          new MyObject(Any.anyInteger().nonnegative(), null);
     }
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*han 3

我的偏好是构建一个常量实用程序类以及帮助创建测试常量值的方法,例如:

public final class Values {
    public static final int ANY_INT = randomInt(Integer.MIN_VALUE, Integer.MAX_VALUE);
    public static final int ANY_POSITIVE_INT = randomInt(0, Integer.MAX_VALUE);
    public static final String ANY_ISBN = randomIsbn();
    // etc...

    public static int randomInt(int min, int max) { /* omitted */ }
    public static String randomIsbn() { /* omitted */ }

    // etc...
}
Run Code Online (Sandbox Code Playgroud)

然后,我将使用静态导入来提取特定测试类所需的常量和方法。

ANY_我仅在不关心值的情况下使用常量,我发现它们可以使测试的意图更清晰,例如:

// when
service.fooBar(ANY_INT, ANY_INT, ANY_INT, ANY_INT, 5);
Run Code Online (Sandbox Code Playgroud)

很明显,该值5具有一定的意义 - 尽管作为局部变量会更好。

实用程序方法可用于在设置测试时临时生成值,例如:

// given
final String isbn1 = randomIsbn();
final String isbn2 = randomIsbn();
final Book[] books = { new Book(isbn1), new Book(isbn2) };

// when
bookRepository.store(books);
Run Code Online (Sandbox Code Playgroud)

同样,这可以帮助测试类关注测试本身,而不是数据设置。

除此之外,我还使用了域对象的类似方法。当你结合这两种方法时,它会非常强大。例如:

public final class Domain {
    public static Book book() {
        return new Book(randomIsbn());
    }
    // etc...
}
Run Code Online (Sandbox Code Playgroud)