如何将 JUnit 参数化运行器与 varargs 构造函数一起使用?

发布于 2024-09-14 05:04:31 字数 1114 浏览 4 评论 0原文

我写了一个模型示例来说明这一点,但没有泄露任何机密。这是一个“虚拟”示例,不执行任何操作,但问题出现在测试初始化​​程序中。

@RunWith(Parameterized.class)
public class ExampleParamTest
{
 int ordinal;
 List<String> strings;

 public ExampleParamTest(int ordinal, String... strings)
 {
  this.ordinal = ordinal;
  if (strings.length == 0)
  {
   this.strings = null;
  }
  else
  {
   this.strings = Arrays.asList(strings);
  }
 }

 @Parameters
 public static Collection<Object[]> data() {
  return Arrays.asList(new Object[][] {
    {0, "hello", "goodbye"},
    {1, "farewell"}
  });
 }

 @Test
 public void doTest() {
  Assert.assertTrue(true);
 }
}

基本上我有一个测试构造函数,它接受本地列表变量的多个参数,并且我想通过数组初始化程序来填充它。测试方法将正确处理本地列表变量 - 我已删除此逻辑以简化测试。

当我写这篇文章时,我的 IDE 对语法没有任何抱怨,并且测试类的构建没有任何编译错误。然而,当我运行它时,我得到:

doTest[0]:
java.lang.IllegalArgumentException: wrong number of arguments
  at java.lang.reflect.Constructor.newInstance(Unknown Source)
doTest[1]:
java.lang.IllegalArgumentException: argument type mismatch
  at java.lang.reflect.Constructor.newInstance(Unknown Source)

这里到底出了什么问题,以及如何正确使用这个模式?

I wrote a mockup example to illustrate this without exposing anything confidential. It's a "dummy" example which does nothing, but the problem occurs in the test initialiser.

@RunWith(Parameterized.class)
public class ExampleParamTest
{
 int ordinal;
 List<String> strings;

 public ExampleParamTest(int ordinal, String... strings)
 {
  this.ordinal = ordinal;
  if (strings.length == 0)
  {
   this.strings = null;
  }
  else
  {
   this.strings = Arrays.asList(strings);
  }
 }

 @Parameters
 public static Collection<Object[]> data() {
  return Arrays.asList(new Object[][] {
    {0, "hello", "goodbye"},
    {1, "farewell"}
  });
 }

 @Test
 public void doTest() {
  Assert.assertTrue(true);
 }
}

Basically I have a test constructor which accepts multiple arguments for a local list variable and I want to populate this through an array initialiser. The test method will handle the local list variable correctly - I have removed this logic to simplify the test.

When I write this, my IDE has no complaints about syntax and the test class builds without any compile errors. However when I run it, I get:

doTest[0]:
java.lang.IllegalArgumentException: wrong number of arguments
  at java.lang.reflect.Constructor.newInstance(Unknown Source)
doTest[1]:
java.lang.IllegalArgumentException: argument type mismatch
  at java.lang.reflect.Constructor.newInstance(Unknown Source)

What exactly has gone wrong here, and how do I correctly use this pattern?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

太阳哥哥 2024-09-21 05:04:31

现在无法测试它,但我想,如果您使用可变参数调用方法或构造函数,则必须使用数组而不是变量值列表来调用它。

如果我是对的,那么这应该可行:

@Parameters
 public static Collection<Object[]> data() {
  return Arrays.asList(new Object[][] {
    {0, new String[]{"hello", "goodbye"}},
    {1, new String[]{"farewell"}}
  });
 }

一些解释

在源代码级别,我们可以编写

test = ExampleParamTest(0, "one", "two");

编译器会将其转换为字符串数组。 JUnit 使用反射和调用 API,从这个角度来看,构造函数签名是

public ExampleParamTest(int i, String[] strings);

So 要调用构造函数 - 这就是 JUnit 在内部所做的 - 您必须传递一个整数和一个字符串数组。

Can't test it right now but I guess, if you invoke a method or a constructor with variable arguments, you have to invoke it with an array instead of a variable list of values.

If I'm right, then this should work:

@Parameters
 public static Collection<Object[]> data() {
  return Arrays.asList(new Object[][] {
    {0, new String[]{"hello", "goodbye"}},
    {1, new String[]{"farewell"}}
  });
 }

Some explanation

On source code level, we can write

test = ExampleParamTest(0, "one", "two");

The compiler will convert this to an array of Strings. JUnit uses the reflection and invocation API, and from this perspective, the constructors signature is

public ExampleParamTest(int i, String[] strings);

So to invoke the constructor - and that's what JUnit is doing internally - you have to pass an integer and a String array.

断念 2024-09-21 05:04:31

@marcphillip 在此 JUnit 功能请求 中给出的解决方法目前效果很好:

@ParameterizedTest
@CsvSource({"1,a", "1,a,b,c"})
void testAbc(int arg1, @AggregateWith(VarargsAggregator.class) String... elements) {
    System.out.println(Arrays.toString(elements));
}

static class VarargsAggregator implements ArgumentsAggregator {
    @Override
    public Object aggregateArguments(ArgumentsAccessor accessor, ParameterContext context) throws ArgumentsAggregationException {
        Class<?> parameterType = context.getParameter().getType();
        Preconditions.condition(parameterType.isArray(), () -> "must be an array type, but was " + parameterType);
        Class<?> componentType = parameterType.getComponentType();
        return IntStream.range(context.getIndex(), accessor.size())
                .mapToObj(index -> accessor.get(index, componentType))
                .toArray(size -> (Object[]) Array.newInstance(componentType, size));
    }
}

更新:我添加了此调整,以便在最后一个逗号后没有提供任何内容时提供一个空数组:更新2: > 我意识到这是不需要的:只需省略尾随逗号(我认为这是强制性的)即可传递空的可变参数数组。

        if (result.length == 1 && result[0] == null) {
            return Array.newInstance(componentType, 0);
        }
        return result;

The workaround given by @marcphillip in this JUnit feature request works great for now:

@ParameterizedTest
@CsvSource({"1,a", "1,a,b,c"})
void testAbc(int arg1, @AggregateWith(VarargsAggregator.class) String... elements) {
    System.out.println(Arrays.toString(elements));
}

static class VarargsAggregator implements ArgumentsAggregator {
    @Override
    public Object aggregateArguments(ArgumentsAccessor accessor, ParameterContext context) throws ArgumentsAggregationException {
        Class<?> parameterType = context.getParameter().getType();
        Preconditions.condition(parameterType.isArray(), () -> "must be an array type, but was " + parameterType);
        Class<?> componentType = parameterType.getComponentType();
        return IntStream.range(context.getIndex(), accessor.size())
                .mapToObj(index -> accessor.get(index, componentType))
                .toArray(size -> (Object[]) Array.newInstance(componentType, size));
    }
}

Update: I added this tweak to give an empty array when there is nothing provided after the last comma: Update2: I realized this is not needed: Just leave off the trailing comma (which I'd assumed was mandatory) in order to pass in an empty vararg array.

        if (result.length == 1 && result[0] == null) {
            return Array.newInstance(componentType, 0);
        }
        return result;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文