开源 Auto generate mock data for java test.(便于 Java 测试自动生成对象信息)
开源 Junit performance rely on junit5 and jdk8+.(java 性能测试框架。性能测试。压测。测试报告生成。)
jqwik的发音类似于 “jay quick” [?d?e?kw?k]。
jqwik的主要目的是将属性驱动测试(Property-Based Testing,PBT)引入JVM。
该库主要专注于Java和Kotlin,同时也支持Groovy。
属性驱动测试试图将微测试的直观性与随机生成的测试数据的有效性相结合。
最初受到对函数式编程的普遍热情的推动,PBT现在被认为是任何现代化测试方法的重要组成部分。
属性旨在描述代码的通用不变性或后置条件,给定某些前提条件。
测试库jqwik将尝试生成许多满足前提条件的值集,希望生成的集合中的一个能够证明错误的假设。
以下属性涉及于(臭名昭著的)Fizz Buzz Kata的部分实现:
前提条件:考虑介于1和100之间可被3整除的数字。
后置条件:由fizzBuzz()返回的字符串以Fizz开头。
import java.util.*;
import java.util.stream.*;
import net.jqwik.api.*;
class FizzBuzzTests {
@Property
boolean every_third_element_starts_with_Fizz(@ForAll("divisibleBy3") int i) {
return fizzBuzz().get(i - 1).startsWith("Fizz");
}
@Provide
Arbitrary<Integer> divisibleBy3() {
return Arbitraries.integers().between(1, 100).filter(i -> i % 3 == 0);
}
private List<String> fizzBuzz() {
return IntStream.range(1, 100).mapToObj((int i) -> {
boolean divBy3 = i % 3 == 0;
boolean divBy5 = i % 5 == 0;
return divBy3 && divBy5 ? "FizzBuzz"
: divBy3 ? "Fizz"
: divBy5 ? "Buzz"
: String.valueOf(i);
}).collect(Collectors.toList());
}
}
通过使用一些注解,jqwik 试图使程序员尽可能简单地编写和运行属性测试。
这是2009年。在进行了大约两个十年的主要面向对象编程之后,对于函数式语言将极大简化编写正确并发程序的说法引起了我的兴趣,我开始研究Clojure、Erlang,最终是Haskell。
时至今日,2018年,我在并发方面的活动有些减弱,但对软件开发的函数式一面的好奇心一直伴随着我。
作为一个开发者,我一直在贯彻测试驱动开发的理念,因此当我试图了解“其他人”是如何工作和思考的时候,一个社区对测试的方法是我首先关注的事情之一。
这就是我了解到函数式编程人员非常喜欢属性测试,与我们面向对象的程序员所称的单元测试相对立。
这种视角的改变不仅在强类型语言(如Haskell)中可见,而且在动态类型语言(如Erlang)中也可见。
然而,当涉及赚钱时,Java、Groovy,以及在较小程度上的JavaScript仍然是我的主要工作工具。
因此,我开始思考属性驱动测试(PBT)是否也可以在这里帮助我。在DuckDuckGo上搜索“Java”和“Property-based Testing”会给你一些结果 - 有些可以追溯到JUnit的Theories-Runner仍然存在的时候 - 但在网上找到的信息远远少于我期望的。一些人,如Nat Price,尝试在Java中实验这种方法,但也仅此而已。
可以说是一个幸运的巧合,在两年前的这个时候,我离开了JUnit-5核心团队,但决定尝试JUnit平台。
这就是为什么我创建了一个名为jqwik的测试引擎,专注于属性驱动测试。
除了深入研究Java对反射的完全混乱的处理方式之外,这项任务还要求我尝试使用不同的方法编写属性,研究其他属性驱动测试库的工作原理,并找到一种将这种新类型的测试整合到我习惯的TDD风格中的方法。
在接下来的几周中,我将发布一系列博客文章,描述我在这个过程中学到的有关属性驱动测试的本质以及如何在Java中使用它。
到目前为止,我已经发布了八个部分:
属性驱动测试(Property-Based Testing)和常规测试方法之间存在一些显著的优缺点差异。以下是它们的比较:
广泛覆盖: 属性驱动测试可以生成大量的随机输入,从而更全面地覆盖输入空间,包括边界情况和边缘情况,帮助发现隐藏的错误。
自动化: 由于测试框架会自动生成和执行测试用例,因此减少了手动编写大量测试用例的工作,提高了测试的自动化水平。
发现性测试: 通过引入随机性,属性驱动测试可以发现开发者可能未考虑到的测试场景,帮助发现潜在的问题。
灵活性: 属性驱动测试更加灵活,因为测试用例是通过属性定义而不是具体的输入值,这使得测试更容易适应代码变更。
难以理解: 对于初学者来说,属性驱动测试可能更难理解,因为它要求开发者定义和理解系统属性,这可能需要更多的学习和实践。
不易调试: 当测试失败时,属性驱动测试可能会给出一个使测试失败的最小输入,但这可能不容易理解,因为这个输入是通过缩小算法生成的。
性能开销: 生成大量的随机测试用例可能导致性能开销,特别是对于复杂的系统,可能需要更多的时间来执行测试。
直观和简单: 常规测试方法相对来说更直观和简单,开发者只需为特定的输入编写测试用例。
易于理解和调试: 常规测试用例通常更容易理解和调试,因为它们是直接指定的输入和预期输出。
定制性高: 开发者可以根据需要编写特定的测试用例,以检查特定的功能或场景。
覆盖范围有限: 常规测试方法可能无法覆盖系统的所有可能情况,因为手动编写所有可能的测试用例可能很困难。
维护成本高: 随着系统的增长,手动维护大量的测试用例可能会变得繁琐,尤其是在代码变更时需要更新测试。
无法发现隐藏问题: 常规测试可能无法轻松发现一些隐藏的问题或未考虑到的场景,因为测试用例通常是基于开发者的直觉编写的。
在实际项目中,通常会综合使用这两种测试方法,以发挥它们各自的优势,提高测试的全面性和效率。
https://jqwik.net/
https://blog.johanneslink.net/2018/03/24/property-based-testing-in-java-introduction/
https://jqwik.net/docs/current/user-guide.html