JUnit框架功能详细(Rule、Assume、Assert)——JUnit学习(二)

书接上文,之前已经介绍了JUnit一些常用的功能,接下来完成Rule、Assume、Assert的介绍。



Rule

JUnit4中包含两个注解@Rule和@ClassRule用于修饰Field或返回Rule的 Method,Rule是一组实现了TestRule接口的共享类,提供了验证、监视TestCase和外部资源管理等能力。JUnit提供了以下几个Rule实现,必要时也可以自己实现Rule。


  • Verifier: 验证测试执行结果的正确性。
  • ErrorCollector: 收集测试方法中出现的错误信息,测试不会中断,如果有错误发生测试结束后会标记失败。
  • ExpectedException: 提供灵活的异常验证功能。
  • Timeout: 用于测试超时的Rule。
  • ExternalResource: 外部资源管理。
  • TemporaryFolder: 在JUnit的测试执行前后,创建和删除新的临时目录。
  • TestWatcher: 监视测试方法生命周期的各个阶段。
  • TestName: 在测试方法执行过程中提供获取测试名字的能力。
简单的说就是提供了测试用例执行过程中一些通用功能的共享的能力,使我们不必重复编写一些功能类似的代码。JUnit用于标注Rule的注解包括@Rule和@ClassRule,区别在于作用域不同@Rule的作用域是测试方法,@ClassRule则是测试Class。下面简单介绍下上述Rule的使用。
Verifier:  是校验功能的Rule,你可以实现Verifier的verify方法,在测试方法执行后(无异常的话)会调用verify方法校验测试结果(如文件、结果是否更改等),与预期结果不符时抛出异常导致当前测试失败。
ErrorCollector: 继承自Verifier。用于Test Method执行过程中收集错误,不会中断Test Method的执行,如果捕获到错误会最终导致测试失败。个人感觉ErrorCollector的用途比较有限,因为收集错误的代码都比较复杂不是很实用,一些特殊场景(测试方法很复杂、存在多项测试并且启动测试的开销比较大)可能使用到。ErrorCollector提供的收集方法有三个:
  • collector.addError(Throwable):这是最简单的收集方式,可以用在try...catch中,记录发生的错误。
  • collector.checkSucceeds(Callable<Object>()):支持调用callable的call方法,是同步的。一旦Callable抛出异常ErrorCollector会记录并最终导致失败。
  • collector.checkThat(T value, Matcher<T> matcher):测试value是否匹配Matcher,如果不匹配会记录matcher提供的错误信息。

ExpectedException:异常测试,在上一篇文章中介绍的@Test(expected=xxx)用法本质就是利用了这个Rule。相比之前的用法ExpectedException提供了灵活匹配规则,可以根据message、cause和异常的具体类型匹配。以下代码分别测试了message、异常类型和cause匹配:

public class ExpectedExceptionTest
{

	@Rule
	public ExpectedException exp = ExpectedException.none();
	
	@Test
	public void expectException()
	{
		exp.expect(IndexOutOfBoundsException.class);
		throw new IndexOutOfBoundsException("Exception method.");
	}

	@Test
	public void expectMessage()
	{
		exp.expectMessage("Hello World");
		throw new RuntimeException("Hello World will throw exception.");
	}

	@Test
	public void expectCourse()
	{
		exp.expectCause(new BaseMatcher<IllegalArgumentException>()
		{

			public boolean matches(Object item)
			{
				return item instanceof IllegalArgumentException;
			}

			public void describeTo(Description description)
			{
				description.appendText("Expected Cause Error.");
			}

		});
		
		Throwable cause = new IllegalArgumentException("Cause Test.");
		throw new RuntimeException(cause);
	}
}

Timeout:这是一个很简单的Rule,用来控制method的执行时间。在上一篇文章中的@Test(timeout=xxx)就是由这个Rule实现的两者效果一致。值得一提的是Timeout用@ClassRule注解会有惊喜,因为作用域变成了Class所以它可以控制整个Test Class执行的耗时,也就是累计时间。
ExternalResource:这是一个抽象类,顾名思义这是一个管理测试需要的外部资源的类。一般情况下多使用@ClassRule来修饰,使用场景包括准备数据、启动服务器、创建测试文件等操作,下面代码模拟了为测试准备数据的情形:

@ClassRule
	public static ExternalResource external = new ExternalResource()
	{
		protected void before() throws Throwable
		{
			System.out.println("Perparing test data.");
			System.out.println("Test data is Ready!");
		}

		protected void after()
		{
			System.out.println("Cleaning test data.");
		}
	};
	
	@Test
	public void method1()
	{
		System.out.println("Test Method first executing...");
	}
	
	@Test
	public void method2()
	{
		System.out.println("Test Method second executing...");
	}
	
	@Test
	public void method3()
	{
		System.out.println("Test Method thrid executing...");
	}
执行后输出结果:
Perparing test data.
Test data is Ready!
Test Method first executing...
Test Method second executing...
Test Method thrid executing...
Cleaning test data.
TemporaryFolder:临时目录?这个Rule实现自ExternalResource,是其的具体实现,它允许你在测试过程中随时创建需要的临时目录,并在测试结束时自动删除所有临时目录。一个简单测试如下:
	@ClassRule
	public static TemporaryFolder folderCreater = new TemporaryFolder();
	
	@Before
	public void before() throws IOException
	{
		folderCreater.create();
	}

	public void createFolder() throws IOException
	{
		File folder = folderCreater.newFolder();
		File file = folderCreater.newFile("fileName.txt");
		//do something...
	}
TestWatcher:提供了一个用于监视测试执行的基类。TestWatcher及其子类不会改变测试的任何行为,提供了succeeded、failded、skipped、starting、finished方法监控一个测试方法生命周期的各个阶段,所有方法都包含一个org.junit.runner.Description类型的参数描述了当前执行的测试。
TestName:TestName继承自TestWatcher,提供了获取每个测试名称的能力。TestName有什么应用场景不好说,不过确实是个不错的实现了TestWatcher的样例:
public class TestName extends TestWatcher {
    private String fName;

    @Override
    protected void starting(Description d) {
        fName = d.getMethodName();
    }

    /**
     * @return the name of the currently-running test method
     */
    public String getMethodName() {
        return fName;
    }
}

以上就是TestName的所有代码,它提供了一个getMethodName的方法你可以在测试方法的任何地方调用这个方法获取当前的测试方法名称(虽然没什么用),这个方法是通过重载TestWatcher的starting方法实现的。


Assume


Assume直译为假设,是JUnit提供的一套用于判断测试用例的入参是否有业务含义的工具,如果入参不符合预期时会抛出AssumptionViolatedException,默认的BlockJUnit4ClassRunner及其子类会捕获这个异常并跳过当前测试,如果使用自定义的Runner则无法保证行为,视Runner的实现而定。
Assume提供的验证方法包括: assumeTrue/assumeFalse、 assumeNotNull、 assumeThat、 assumeNoException 。具体含义都比较简单。假设我们有一个打印姓名和年龄的测试用例,使用Theories提供多套参数测试,由于实际年龄不存在负数所以使用Assume排除不合理的数据:
@RunWith(Theories.class)
public class AssumeTest
{

	@DataPoints
	public static String[] names = {"LiLei", "HanMeiMei"};
	
	@DataPoints
	public static int[] ages = {10, -2, 12};
	
	@Theory
	public void printAge(String name, int age)
	{
		Assume.assumeTrue(age > 0);
		System.out.println(String.format("%s's Name is %s.", name, age));
	}
}
打印结果中排除了负数的年龄,并且JUnit测试通过:
LiLei's Name is 10.
LiLei's Name is 12.
HanMeiMei's Name is 10.
HanMeiMei's Name is 12.

Assert

Assert是JUnit提供的断言类,用于常用的测试结果验证。提供的功能和方法都比较简单实用,这里只用列表简单介绍:
AssertTrue、AssertFalse:结果的true、false。
AssertThat:使用Matcher做自定义的校验。
AssertEquals、AssertNotEquals:判断两个对象是否相等。
AssertNull、AssertNotNull:判断对象是否为空。
AssertSame:判断两个对象是否为同一个,不同于equals这里是使用“==”判断。
AssertArrayEquals:判断两个数组是否相等。

至此总结了JUnit4的主要功能的使用方法,后续计划再有一篇博文记录自己在阅读JUnit4原码的一些收获和感想。






你可能感兴趣的:(JUnit框架功能详细(Rule、Assume、Assert)——JUnit学习(二))