1.5分钟学会用Junit参数化测试

前几天在介绍mockito时候, 提到过一点junit5里的参数化测试

1.5分钟学会用Junit参数化测试

今天详细来介绍下这东西的用法.

简介

参数化测试, 俗称Parameterized Tests, 相对于普通测试, 参数化测试的主要特点在于输入和输出数据参数化.

普通的测试代码写起来像是这样的:

试试看 1 + 1 == 2
试试看 1 + 2 == 3

有了参数化测试, 代码写起来就更像是这样的:

试试 a + b == c, 变量从****取

文化水平,就从刚学过算术的小学生, 突然上升到学了代数的初中生了.

最简单的作品

最简单的参数化测试, 这么写就可以.

@ParameterizedTest
void lessThanTen(int n) {
    assertTrue(n < 10);
}

这里添加了一个@ParameterizedTest注解, 这个测试就是参数化测试了, 接收一个参数int.

最简单的参数来源

当然, 前面的测试不能跑, 因为他没有数据来源(source). 最简单的指定方式, 就是@ValueSource, 直接传入一份数据.

@ValueSource(ints = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 })
@ParameterizedTest
void lessThanTen(int n) {
    assertTrue(n < 10);
}

这个测试基本就算是成型了, 用了最简单的参数源.

方法参数源

我们也可以用动态性更强的参数源, 比如上次用过的函数作为来源@MethodSource.

@ParameterizedTest
@MethodSource(value = "addSource")
void add(int a, int b, int expected) {
    assertEquals(a + b, expected);
}

static Stream<Arguments> addSource() {
    return IntStream.rangeClosed(1100)
            .boxed()
            .map(n -> Arguments.of(n, n, 2 * n));
}

这次没有用同名函数,而是传了函数名. 关键就在于加上一个静态函数返回可迭代的内容或者Stream作为数据源.

高级数据源

除了前面的两个, 还有很多其他的source可以使用,不过我更喜欢的是 用@JSONSource.

用法如下:

准备数据文件,jsondata.json.

[
  "1",
  "2",
  "3",
  "4"
]

准备测试, 然后指向参数json文件

@ParameterizedTest
@JSONSource("jsondata.json")
void json(String s) {
    log.info(s);
    assertNotNull(s);
}

如果参数复杂化,也可以,通过注解指定参数类型,例子如下.


@ParameterizedTest
@JSONSource(value = "pojo.json", type = A.class)
void pojo(A a) {

    log.info("data {}", a);
    assertNotNull(a);
    assertEquals(a.a+a.b, a.c);
}

这时候的数据文件如下:

[
  {
    "a"1,
    "b"2,
    "c"3
  },
  {
    "a"1,
    "b"5,
    "c"6
  },
  {
    "a"7,
    "b"2,
    "c"5
  },
  {
    "a"4,
    "b"2,
    "c"6
  }
]

这时候参数化测试的能力一样是可以用的. 而且这里最大的好处是,在升级JDK用上 text block之前, 写json入参可以容易一点点.JSON拼接多行JSON简直是噩梦。

注解内容

当然前面的注解在Junit里找不到, 因为这玩意是我自己写的.

junit这里提供了强大的自定义能力,我们只需要添加1个注解一个注解处理类就行.(如果不想自定义注解, 直接使用处理类也可以).

刚刚用的2个类如下:

import org.junit.jupiter.params.provider.ArgumentsSource;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
//关键在这句, 指定provider是谁。 单独用到方法上也行。
@ArgumentsSource(JSONArgumentsProvider.class)
public @interface JSONSource 
{
    String value();
    Class type() default  String.class;
}
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.support.AnnotationConsumer;
//偷懒用了spring的classpath加载。
import org.springframework.core.io.ClassPathResource;

import java.io.File;
import java.util.List;
import java.util.stream.Stream;

//后一个接口是为了传参数, 消化掉注解传入的类型。
public class JSONArgumentsProvider implements ArgumentsProviderAnnotationConsumer<JSONSource{
    String jsonFileName;
    Class type;

    @Override
    public Stream<? extends Arguments> provideArguments(ExtensionContext extensionContext) throws Exception {
        ClassPathResource resource = new ClassPathResource(jsonFileName);
        File file = resource.getFile();
        List<?> list = new ObjectMapper().readerForListOf(type).readValue(file);
        return list.stream().map(Arguments::of);
    }

    @Override
    public void accept(JSONSource jsonSource) {
        jsonFileName = jsonSource.value();
        type = jsonSource.type();
    }
}

后话

好了, 今天关于Junit5 参数化测试的介绍就到这里,详情最好还是看文档,https://junit.org/junit5 我这里只是极端简化了一下。希望对你有用。1.5分钟学会用Junit参数化测试

原文始发于微信公众号(K字的研究):1.5分钟学会用Junit参数化测试

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/41696.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!