JUnit与JavaScript和SVN一起是程序员经常开始使用的一些技术,甚至没有读过一篇博客文章,更不用说一本书了。 也许这是一件好事,因为它们看起来足够简单且易于理解,因此我们无需任何手册即可立即使用它们,但这也意味着它们还没有得到充分利用。 在本文中,我们将介绍一些我认为非常有用的JUnit功能。
参数化测试
有时我们需要使用许多不同的输入和不同的预期结果来运行相同的方法或功能。 一种方法是为每种情况创建单独的测试,或者您可以使用循环,但是要跟踪可能的测试失败的源将更加困难。
例如,如果我们有以下表示有理数的值对象:
public class RationalNumber {private final long numerator;private final long denominator;public RationalNumber(long numerator, long denominator) {this.numerator = numerator;this.denominator = denominator;}public long getNumerator() {return numerator;}public long getDenominator() {return denominator;}@Overridepublic String toString() {return String.format("%d/%d", numerator, denominator);}
}
我们有一个名为App的服务类,其中包含一个方法转换,该方法将数字除为四舍五入的十进制小数:
public class App {/*** THE Logic** @param number some rational number* @return BigDecimal rounded to 5 decimal points*/public static BigDecimal convert(RationalNumber number) {BigDecimal numerator = new BigDecimal(number.getNumerator()).setScale(5, RoundingMode.HALF_UP);BigDecimal result = numerator.divide(new BigDecimal(number.getDenominator()),RoundingMode.HALF_UP);return result;}
}
对于实际的AppTest类,我们有
@RunWith(Parameterized.class)
public class AppTest {private RationalNumber input;private BigDecimal expected;public AppTest(RationalNumber input, BigDecimal expected) {this.input = input;this.expected = expected;}@Parameterized.Parameters(name = "{index}: number[{0}]= {1}")public static Collection<Object> data() {return Arrays.asList(new Object[][]{{new RationalNumber(1, 2), new BigDecimal("0.50000")},{new RationalNumber(1, 1), new BigDecimal("1.00000")},{new RationalNumber(1, 3), new BigDecimal("0.33333")},{new RationalNumber(1, 5), new BigDecimal("0.20000")},{new RationalNumber(10000, 3), new BigDecimal("3333.33333")}});}@Testpublic void testApp() {//given the test data//whenBigDecimal out = App.convert(input);//thenAssert.assertThat(out, is(equalTo(expected)));}}
参数化的运行器或@RunWith(Parameterized.class)启用“参数化”,换句话说,将使用@ Parameterized.Parameters注释的值的集合注入到Test构造函数中,其中每个子列表都是一个参数列表。 这意味着data()方法中的每个RationalNumber对象将被注入到输入变量中,而每个BigDecimal值将是期望值,因此在我们的示例中,我们有5个测试。
注释中还添加了对生成的测试的可选自定义命名,因此“ {index}:number [{0}] = {1} ”将被data()方法中定义的适当参数和“ {index}”占位符将成为测试用例索引,如下图所示
在IntelliJ Idea中运行参数化测试 |
JUnit规则的最简单定义是,它们在某种意义上是拦截器,并且与面向Spring方面的编程或Java EE拦截器API非常相似。 基本上,您可以在执行测试之前和之后做一些有用的事情。
好的,让我们从一些内置的测试规则开始。 其中之一是ExternalResource ,其想法是我们设置一个外部资源,并在拆卸小工具后释放该资源。 这种测试的一个典型例子是创建文件,因此,我们有一个内置的TemporaryFolder类,但我们也可以为其他资源创建自己的类:
public class TheRuleTest {@Rulepublic TemporaryFolder folder = new TemporaryFolder();@Testpublic void someTest() throws IOException {//givenfinal File tempFile = folder.newFile("thefile.txt");//whentempFile.setExecutable(true) ;//thenassertThat(tempFile.canExecute(), is(true));}
}
我们本可以在@Before和@After块中完成此操作,并使用Java临时文件,但在某些测试失败的情况下,很容易忘记某些东西,并且未关闭某些文件。
例如,还有一个方法的超时规则,如果在给定的时间限制内执行未完成,则测试将失败,并出现超时异常。 例如,将运行时间限制为20毫秒:
@Rule
public MethodRule globalTimeout = new Timeout(20);
我们可以实施自己的规则,以执行政策或进行各种项目特定的更改。 唯一需要做的就是让我们实现TestRule接口。
解释该行为的一个简单方案是添加一个在测试之前和之后都打印一些东西的规则。
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;public class MyTestRule implements TestRule {public class MyStatement extends Statement {private final Statement statement;public MyStatement(Statement statement) {this.statement = statement;}@Overridepublic void evaluate() throws Throwable {System.out.println("before statement" );statement.evaluate();System.out.println("after statement");}}@Overridepublic Statement apply(Statement statement,Description description) {System.out.println("apply rule");return new MyStatement(statement);}}
因此,现在有了我们的规则,我们可以在测试中使用它了,因为测试只会打印出不同的值:
public class SomeTest {@Rulepublic MyTestRule folder = new MyTestRule();@Testpublic void testA() {System.out.println("A");}@Testpublic void testB() {System.out.println("B");}
}
当我们运行测试时,将在控制台输出上创建以下输出:
apply rule
before statement
A
after statement
apply rule
before statement
B
after statement
内置的一个名为ExpectedException的异常在尝试测试错误时非常有用。 此外,还有一个选项可以链接规则,这些规则在许多情况下都非常有用。
总结一下
如果您想说Spock或TestNG或在JUnit之上构建的某些库比JUnit具有更多的功能,那可能就是事实。
但是你知道吗? 我们的课程路径上并不总是有那些,而且JUnit在那里并已经在各处使用的可能性很大。 为什么不利用它的全部潜力呢?
翻译自: https://www.javacodegeeks.com/2013/12/run-junit-run.html