Java中测试用例中的Arguments.of():它是如何工作的?

huangapple go评论65阅读模式
英文:

Java Arguments.of() in a test case: How does it work?

问题

我已经收到了一些参数化的测试用例,并且这些测试用例使用了Stream.of和Arguments.of,这些我不太熟悉。如下所示,测试用例引用了我的BofAAdapter类的构造函数,并传递了一个BofA对象。我想知道如何在我的类中访问整数(500)和条目的映射,或者它们是在哪里传递的。我无法编辑测试用例,因此不能简单地让它们使用不同的构造函数进行测试。我对这个测试在做什么感到非常困惑。

@ParameterizedTest
@MethodSource("providerBankAccountsGetDelinquent")
public void testBankAccountsGetDelinquent(IBankAccounts adapter, int threshold, Map<Integer, Integer> expected) {
    Map<Integer, Integer> actual = adapter.getDelinquent(threshold);
    assertEqualMaps(expected, actual);
}

static Stream<Arguments> providerBankAccountsGetDelinquent() {
    return Stream.of(
        Arguments.of(new BofAAdapter(new BofA()), 500,
            Map.ofEntries(
                Map.entry(1, 500),
                Map.entry(5, 20)
            )),
英文:

I've been given test cases that are parameterized and use Stream.of and Arguments.of which I am not familiar with. As evidenced below the test case references the constructor of my class BofAAdapter and passes a BofA object. I'm wondering how to access in my class the integer (500) and the map of entries, or where they are being passed. I can't edit the test cases so I can't simply have them test with a different constructor. I'm just pretty lost here with what this test is doing.

@ParameterizedTest
	@MethodSource(&quot;providerBankAccountsGetDelinquent&quot;)
	public void testBankAccountsGetDelinquent(IBankAccounts adapter, int threshold, Map&lt;Integer, Integer&gt; expected) {
		Map&lt;Integer, Integer&gt; actual = adapter.getDelinquent( threshold );
		assertEqualMaps( expected, actual );
	}
	static Stream&lt;Arguments&gt; providerBankAccountsGetDelinquent() {
		return Stream.of(
				Arguments.of( new BofAAdapter( new BofA() ), 500, 
						Map.ofEntries(
							Map.entry( 1, 500),
							Map.entry( 5,  20)
						)),

答案1

得分: 3

Arguments 是来自junit 5的一个概念。

您可以阅读关于 Arguments 的内容以及如何使用它,以及上述链接的 junit 网站中正在发生的情况。

简要概述一下:通常,测试方法没有参数。测试框架(junit5)会看到 @Test 注解,然后直接运行它。

但这是一个不同的情况:这个测试方法有参数!试着换个角度思考一下测试框架:您会如何调用它?对于 'adapter' 和 'treshold',您会传递什么?

这就是 @MethodSource 注解的作用:它在告诉 junit:首先,运行 providerBankAccountsGetDelinquent 方法,这是 junit 可以做到的,因为没有要传递的参数。然后,获取此方法提供的参数,这是一个流(因此是 Arguments 类的任意数量实例),并且对于每个获得的 Arguments 对象,运行此测试方法一次。

所以,最终发生的情况是:

  1. junit 调用 providerBankAccountsGetDelinquent
  2. junit 获得一个 Stream<Arguments> 对象。
  3. junit 开始遍历这个流。
  4. 流将返回它将要返回的第一个(也是唯一的)对象:一个 Arguments 对象,其中 threshold = 500,一个新的 BofAAdapter 等。
  5. 然后,它将使用这些参数调用 testBankAccountsGetDelinquent,并根据该测试的结果(取决于这些映射是否相等)打印 'succeed' 或 'fail'。
  6. junit 向流请求下一个对象。
  7. 流说:没有了,我只有那个 Arguments 对象。
  8. 测试序列的这部分现在已经完成。

换句话说,所有这些代码只是写下这种复杂且愚蠢的方式:

@Test
public void testBankAccountsGetDelinquent() {
    IBankAccounts adapter = new BofAAdapter(new BofA());
    int threshold = 500;
    Map<Integer, Integer> expected = Map.ofEntries(
                            Map.entry(1, 500),
                            Map.entry(5, 20)
                        );
    Map<Integer, Integer> actual = adapter.getDelinquent(threshold);
    assertEqualMaps(expected, actual);
}

当然,假设这段代码要么是一个示例,用于展示 @ParameterizedTest 如何工作,要么有人计划扩展 providerBankAccountsGetDelinquent

请注意,总体思路是您可以使用 @ParameterizedTest 机制来创建动态输入。想象一下,例如,您想要对从 400 到 500 的所有阈值运行此测试,并且映射也在变化。那么这是一种方法。

英文:

Arguments is a concept from junit 5.

You can read about what Arguments is about and how to use it, and what's happening here over at the junit site linked above.

To give you a quick overview: normally, a test method has no arguments. The test framework (junit5) will see a @Test annotation and will just run it.

But this is a different beast: This test method has.. parameters! Walk in the shoes of the test framework for a moment: How would you invoke this? What do you pass for 'adapter' and 'treshold'?

That's what the @MethodSource annotation is about: It's telling junit: FIRST, run the providerBankAccountsGetDelinquent method, which junit can do, because there are no arguments to pass. Then take the arguments that this method gave you, which is a stream (so, any number of instances of the Arguments class), and run this test method once for each Arguments object you got.

So, this ends up happening:

  1. junit calls providerBankAccountsGetDelinquent
  2. junit gets a Stream&lt;Arguments&gt; object.
  3. junit will start iterating through the stream.
  4. The stream will return the first (and only) object it will ever return: An Arguments object with threshold = 500, a new BofAAdapter, etc.
  5. It will then invoke testBankAccountsGetDelinquent with those arguments and print 'succeed' or 'fail' depending on the result of that test (which will depend on whether those maps are equal)
  6. junit asks the stream for the next object.
  7. The stream says: Nope, I'm all out, that one Arguments object is all I had.
  8. This part of the testing sequence is now complete.

In other words, all that code is a convoluted and silly way to just write:

@Test
public void testBankAccountsGetDelinquent() {
    IBankAccounts adapter = new BofAAdapter( new BofA() );
    int threshold = 500;
    Map&lt;Integer, Integer&gt; expected = Map.ofEntries(
                            Map.entry( 1, 500),
                            Map.entry( 5,  20)
                        );
    Map&lt;Integer, Integer&gt; actual = adapter.getDelinquent( threshold );
    assertEqualMaps(expected, actual);
}

Of course, presumably this code is either an example to show you how @ParameterizedTest works, or someone has a plan to expand on providerBankAccountsGetDelinquent.

Note that the general idea is that you use this @ParameterizedTest mechanism to create dynamic inputs. Imagine, for example, you want to run this test for all thresholds from 400 to 500, with the map changing too. Then this is one way to do that.

huangapple
  • 本文由 发表于 2020年10月9日 22:31:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/64282078.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定