当单元测试某些方法时,可能会出现一些情况,某些参数的值不重要,可以是任何值.
例如在这段代码中:
public void method(String arg1,String arg2,int arg3){ if(arg1 == null) throw new NullPointerException("arg1 is null"); //some other code }
单元测试当arg1为null时的行为,则必须抛出NPE,其他参数的值不重要,它们可以是任何值或为空.
所以我想记录一个事实,即这些值对于被测试方法无关紧要.
我想到了以下选项:
选项1:定义ANY_XXX的常量
我想明确地创建常量ANY_STRING和ANY_INT,其中包含一个固定值,它记录它可以是任何值,并且被测方法不关心实际值.
我可以将所有这些常量放在一个名为Any的类中,并在所有测试类中重用它们.
选项2:ANY_XXX的随机值
这个选项对我来说似乎有点黑客,因为我已经读过某个地方,随机性不应该被带入测试用例.但是在这种情况下,这个随机性将不可见,因为参数不会产生任何副作用.
哪种方法更适合更好,可读的测试?
更新:
虽然我可以通过在Any类中定义常量来使用ANY_XXX方法,但是我也在考虑用一些约束来生成ANY_XXX值,例如
Any.anyInteger().nonnegative(); Any.anyInteger().negative(); Any.anyString().thatStartsWith("ab");
我想,也许Hamcrest Matchers可以用来创建这个链接.但是我不知道这种做法是不是很好. anyObject()的类似方法已经由Mockito提供,但这些方法仅适用于Mocks和间谍,而不适用于普通对象.我想为普通对象实现相同的更多可读性测试.
为什么要这样做?
假设我有一个班
class MyObject{ public MyObject(int param1,Object param2){ if(param1 < 0) throw new IllegalArgumentException(); if(param2 == null) throw new NullPointerException(); } }
而现在在编写测试构造函数时
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); } }
解决方法
我的偏好是建立一个实用程序类常量和方法,以帮助创建测试的常量值,例如:
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... }
然后我将使用静态导入来拉取特定测试类所需的常量和方法.
我仅在不关心该值的情况下使用ANY_常量,我发现他们可以使测试的意图更清晰,例如:
// when service.fooBar(ANY_INT,ANY_INT,5);
很明显,值5具有一定的意义 – 虽然它会更好地作为一个局部变量.
// given final String isbn1 = randomIsbn(); final String isbn2 = randomIsbn(); final Book[] books = { new Book(isbn1),new Book(isbn2) }; // when bookRepository.store(books);
同样,这可以帮助测试课程关心测试本身,而不是关于数据设置.
除此之外,我也从域对象中使用了类似的方法.当您结合这两种方法时,可能会非常强大.例如.:
public final class Domain { public static Book book() { return new Book(randomIsbn()); } // etc... }