Java 使用JUnit测试返回多个值的API

Java 使用JUnit测试返回多个值的API,java,unit-testing,junit,Java,Unit Testing,Junit,我想测试一个API,它接收一个参数并返回一个集合。测试使用参数调用API,并检查返回的集是否包含预期值 假设,我必须使用参数arg1、arg2和arg3测试API,并检查返回集中是否出现值a、b、c。也就是说,我的测试用例如下所示: 使用arg1调用API,并检查a, b,c出现在返回的集合中 使用arg2调用API,并检查a, b,c出现在返回的集合中 使用arg3调用API,并检查a, b,c出现在返回的集合中 如何使用JUnit4开发这个测试用例?如果我必须添加arg4?如果我必须检查

我想测试一个API,它接收一个参数并返回一个集合。测试使用参数调用API,并检查返回的集是否包含预期值

假设,我必须使用参数
arg1
arg2
arg3
测试API,并检查返回集中是否出现值
a
b
c
。也就是说,我的测试用例如下所示:

  • 使用
    arg1
    调用API,并检查
    a
    b
    c
    出现在返回的集合中
  • 使用
    arg2
    调用API,并检查
    a
    b
    c
    出现在返回的集合中
  • 使用
    arg3
    调用API,并检查
    a
    b
    c
    出现在返回的集合中
如何使用JUnit4开发这个测试用例?如果我必须添加
arg4
?如果我必须检查返回的集合中是否出现值
d
?我可以从配置中读取参数和期望值的列表吗

我通常会求助于类似这样的东西——它是一个用于声明式编写“匹配器”的库,它与JUnit配合得非常好

然而,他指出,尽管可以通过Hamcrest实现这一点,但更简单的方法是使用java.util.Collection中的方法:

ArrayList<Integer> expected = new ArrayList<Integer>();
expected.add(1); expected.add(2); expected.add(3);

assertTrue(actual.containsAll(expected));
ArrayList应为=新的ArrayList();
增加(1);增加(2);增加(3);
assertTrue(实际包含所有(预期));

假设您想要测试方法“method1”,该方法使用一个参数并返回一个您要编写的集合:

Set result = method1(arg1);
assertTrue(result.contains(a));
assertTrue(result.contains(b));
assertTrue(result.contains(c));
但最好是直接将集合与其预期值进行比较:

Set expected = new HashSet();
expected.add(a);
expected.add(b);
expected.add(c);

assertEquals(expected, method1(arg1));
assertEquals(expected, method1(arg2));
assertEquals(expected, method1(arg3));
当然,如果需要,也不要犹豫使用更通用的循环。

流畅的断言 首先,使用library引入具有有意义的错误消息的好看的断言:

assertThat(method(arg1)).containsExactly(a, b, c);
assertThat(method(arg2)).containsExactly(a, b, c);
assertThat(method(arg3)).containsExactly(a, b, c);
BDD方式 但是我知道你的问题不是关于语法,而是关于方法:如果需要测试
arg4
,你应该怎么做?好吧,如果
arg1
arg4
有不同的语义含义,我建议您对每个参数进行单独的测试非常详细,但可读性也非常强(伪代码):

基于。

方法论:

“敏捷”方式 测试需要不断的开发和重构,就像生产代码一样。同样,雅格尼(“你不需要它”)等原则也适用。如果现在只需要测试
a
b
c
,那么我将从一个普通的硬编码单元测试开始。如果以后你的测试用例开始重复,那么无论如何都要考虑如何重构它们。


或者你现在已经到了这一点,但对我来说,这个问题似乎没有提供足够的信息来给出关于如何重构单元测试的更具体的建议。从XML读取测试?生成组合测试数据?参数化转轮(根据@Tomasz)?也许我还没有完全理解这个问题,但上面提到的问题似乎仍然太抽象了。

谢谢,但我现在还是想继续使用JUnit。@Michael,很抱歉,我最初的回答有点空洞……我现在编辑了它,添加了更详细的建议。再说一次,既然你说你在问方法论,我想这没有抓住重点…谢谢,但我想有一些更通用和动态的东西。这就是为什么我问如果我添加更多的参数和期望值来检查会怎么样。这根本不起作用。按照这里使用的方式,assertEquals()调用将比较对象引用,而不是它们的内容。即使方法按预期工作,它们也总是会失败。要检查更多值吗?只需在“预期”集合中添加更多元素。如果这变得单调乏味,您可以从文件中加载参数和预期结果。JUnit只带来一些商品(断言、测试方法、报告…),其余的只是普通代码。您可以编写与为软件的其他部分编写的通用代码相同的代码。注意,过于笼统会使测试难以理解和维护。因为在没有任何帮助信息的循环中失败的泛型断言不会有多大帮助。@Bohemian,assertEquals方法调用底层的equals方法,该方法应定义为与类型相等相关的任何方法。特别地,JAVA集合重新定义为比比较引用更有用的东西。比较参考资料也是一样的。谢谢。你做对了。我在问有关方法的问题。我的问题是,我不想为所有这些论点编写不同的测试方法。@Michel测试方法学是一门庞大的学科,它取决于许多事情,特别是你测试的东西应该做什么以及如何做。实际上,您应该尽可能少地进行测试,同时检查边缘情况,并确保代码的每个部分至少测试一次。该理论认为,无论如何,你不能测试所有东西,你没有时间,你的测试也会有bug。即使你问的问题对于一个简单的纯函数也是非常具体的,因为如果多次调用,很多东西并不总是返回相同的东西。你展示的例子很好,都是Tomasz,但除了第一个,它们确实很冗长。第一个解决方案只有3行长,但第二个解决方案有33行长(3种写入方法),最后一个解决方案有25行长。软件工程的一个核心原则是KISS(保持简单)。我知道米歇尔希望看到一个好的方法学的例子,但如果没有案例的话,这种方法可能弊大于利。
@Test
public void shouldReturnAbcWhenSomeArgumentUsed() {
  //given
  Object arg = arg1;

  //when
  Set<Object> result = method(arg);

  //then
  assertThat(result).containsExactly(a, b, c);
}
@RunWith(value = Parameterized.class)
public class JunitTest6 {

    private Object arg;

    public JunitTest6(Object arg) {
        this.arg = arg;
    }

    @Parameterized.Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(
                new Object[][]{
                        {arg1},
                        {arg2},
                        {arg3}
                });
    }

    @Test
    public void testMethod() {
        assertThat(method(arg)).containsExcatly(a, b, c);
    }

}