JUnit(四)高级之规则_MD

来源:互联网 发布:淘宝商城铂金会员 编辑:程序博客网 时间:2024/06/08 15:10

Rules[规则]

规则允许在测试类中非常灵活的增加或重新定义每个测试方法的行为。测试人员可以重用或拓展下面提供的规则或编写自己的规则。

我们在使用规则的时候需要使用@Rule注解进行注释,被该注解注释的类需要是TestRule接口的实现。

TestRule接口的实现

  • ErrorCollector: collect multiple errors in one test method
  • ExpectedException: make flexible assertions about thrown exceptions
  • ExternalResource: start and stop a server, for example
  • TemporaryFolder: create fresh files, and delete after test
  • TestName: remember the test name for use during the method
  • TestWatcher: add logic at events during method execution
  • Timeout: cause test to fail after a set time
  • Verifier: fail test if object state ends up incorrect

一、基础规则

1.1 TemporaryFolder[临时文件夹] Rule

TemporaryFolder规则允许创建文件或文件夹,在测试方法(无论是通过还是失败)执行完成时会被删除。默认情况下,如果资源无法被删除是不会抛出异常的:

public static class HasTempFolder {  @Rule  public final TemporaryFolder folder = new TemporaryFolder();  @Test  public void testUsingTempFolder() throws IOException {    File createdFile = folder.newFile("myfile.txt");    File createdFolder = folder.newFolder("subfolder");    // ...  }}
  • TemporaryFolder#newFolder(String... folderNames) 会递归的创建一个临时文件夹。
  • TemporaryFolder#newFile() 创建一个随机名称的文件, 同样 #newFolder() 创建一个随机名称的文件夹
  • 从 4.13 版本 开始,TemporaryFolder optionally allows strict verification of deleted resources which fails the test with AssertionError if resources cannot be deleted. 此功能只能选择用 #builder() 方法。默认情况下禁用严格验证[strict verification],保持向后兼容性[backward compatibility]。
@Rulepublic TemporaryFolder folder = TemporaryFolder.builder().assureDeletion().build();//构建器模式

1.2 ExternalResource[外部资源] Rules

ExternalResource是规则(例如TemporaryFolder)的基础类,在测试之前建立一个外部资源(一个文件,套接字[socket],服务[server],数据库连接,等等),并且保证tear it down afterward。

public static class UsesExternalResource {  Server myServer = new Server();  @Rule  public final ExternalResource resource = new ExternalResource() {    @Override    protected void before() throws Throwable {      myServer.connect();    };    @Override    protected void after() {      myServer.disconnect();    };  };  @Test  public void testFoo() {    new Client().run(myServer);  }}

1.3 ErrorCollector Rule

ErrorCollector 规则允许在第一个问题发现之后继续运行(例如,收集下面不正确的行到一个表中,然后一次性报告所有错误。)

public static class UsesErrorCollectorTwice {  @Rule  public final ErrorCollector collector = new ErrorCollector();  @Test  public void example() {    collector.addError(new Throwable("first thing went wrong"));    collector.addError(new Throwable("second thing went wrong"));  }}

1.4 Verifier Rule

Verifier是规则(例如ErrorCollector)的一个基类, which can turn otherwise passing test methods into failing tests if a verification check is failed.[无论测试的执行情况是什么,只要验证框架不通过,则视为测试失败。]

public static class ErrorLogVerifier {  private ErrorLog errorLog = new ErrorLog();  @Rule  public Verifier verifier = new Verifier() {     @Override     public void verify() {        assertTrue(errorLog.isEmpty());     }  }  @Test public void testThatMightWriteErrorLog() {     // ...  }}

注意:自己测试验证发现,在测试方法执行完成后会执行以下Verifier的verify方法。

1.5 TestWatchman/TestWatcher Rules

TestWatchman 是JUnit 4.7引入的,但是已经过时,被 TestWatcher [4.9引入] 所取代。

TestWatcher (and the deprecated TestWatchman) are base classes for Rules that take note of the testing action, without modifying it. For example, this class will keep a log of each passing and failing test:

TestWatcher 是一个规则的基础类,that take note of the testing action,而不用修改测试类。例如,这个类将保存一个日志在一个通过以及失败的测试:

import static org.junit.Assert.fail;import org.junit.AssumptionViolatedException;import org.junit.Rule;import org.junit.Test;import org.junit.rules.TestRule;import org.junit.rules.TestWatcher;import org.junit.runner.Description;import org.junit.runners.model.Statement;public class WatchmanTest {  private static String watchedLog;  @Rule  public final TestRule watchman = new TestWatcher() {    @Override    public Statement apply(Statement base, Description description) {      return super.apply(base, description);    }    @Override    protected void succeeded(Description description) {      watchedLog += description.getDisplayName() + " " + "success!\n";    }    @Override    protected void failed(Throwable e, Description description) {      watchedLog += description.getDisplayName() + " " + e.getClass().getSimpleName() + "\n";    }    @Override    protected void skipped(AssumptionViolatedException e, Description description) {      watchedLog += description.getDisplayName() + " " + e.getClass().getSimpleName() + "\n";    }    @Override    protected void starting(Description description) {      super.starting(description);    }    @Override    protected void finished(Description description) {      super.finished(description);    }  };  @Test  public void fails() {    fail();  }  @Test  public void succeeds() {  }}

1.6 TestName Rule

TestName 规则使得当前测试的名字在测试内部是可利用的:

public class NameRuleTest {  @Rule  public final TestName name = new TestName();  @Test  public void testA() {    assertEquals("testA", name.getMethodName());  }  @Test  public void testB() {    assertEquals("testB", name.getMethodName());  }}

1.7 Timeout Rule

Timeout规则将同样的时间应用到每一个类。

public static class HasGlobalTimeout {  public static String log;  @Rule  public final TestRule globalTimeout = Timeout.millis(20);  @Test  public void testInfiniteLoop1() {    log += "ran1";    for(;;) {}  }  @Test  public void testInfiniteLoop2() {    log += "ran2";    for(;;) {}  }}

1.8 ExpectedException Rules

The ExpectedException Rule allows in-test specification of expected exception types and messages:

public static class HasExpectedException {  @Rule  public final ExpectedException thrown = ExpectedException.none();  @Test  public void throwsNothing() {  }  @Test  public void throwsNullPointerException() {    thrown.expect(NullPointerException.class);    throw new NullPointerException();  }  @Test  public void throwsNullPointerExceptionWithMessage() {    thrown.expect(NullPointerException.class);    thrown.expectMessage("happened?");    thrown.expectMessage(startsWith("What"));    throw new NullPointerException("What happened?");  }}

二、高级

@ClassRule

The ClassRule annotation extends the idea of method-level Rules, adding static fields that can affect the operation of a whole class. Any subclass of ParentRunner, including the standard BlockJUnit4ClassRunner and Suite classes, will support ClassRules.

For example, here is a test suite that connects to a server once before all the test classes run, and disconnects after they are finished:

@RunWith(Suite.class)@SuiteClasses({A.class, B.class, C.class})public class UsesExternalResource {  public static final Server myServer = new Server();  @ClassRule  public static final ExternalResource resource = new ExternalResource() {    @Override    protected void before() throws Throwable {      myServer.connect();    };    @Override    protected void after() {      myServer.disconnect();    };  };}

RuleChain

The RuleChain rule allows ordering of TestRules:

public static class UseRuleChain {    @Rule    public final TestRule chain = RuleChain                           .outerRule(new LoggingRule("outer rule"))                           .around(new LoggingRule("middle rule"))                           .around(new LoggingRule("inner rule"));    @Test    public void example() {        assertTrue(true);    }}

writes the log

starting outer rulestarting middle rulestarting inner rulefinished inner rulefinished middle rulefinished outer rule

Custom Rules

Most custom rules can be implemented as an extension of the ExternalResource rule. However, if you need more information about the test class or method in question, you’ll need to implement the TestRule interface.

import org.junit.rules.TestRule;import org.junit.runner.Description;import org.junit.runners.model.Statement;public class IdentityRule implements TestRule {  @Override  public Statement apply(final Statement base, final Description description) {    return base;  }}

Of course, the power from implementing TestRule comes from using a combination of custom constructors, adding methods to the class for use in tests, and wrapping the provided Statement in a new Statement. For instance, consider the following test rule that provides a named logger for every test:

package org.example.junit;import java.util.logging.Logger;import org.junit.rules.TestRule;import org.junit.runner.Description;import org.junit.runners.model.Statement;public class TestLogger implements TestRule {  private Logger logger;  public Logger getLogger() {    return this.logger;  }  @Override  public Statement apply(final Statement base, final Description description) {    return new Statement() {      @Override      public void evaluate() throws Throwable {        logger = Logger.getLogger(description.getTestClass().getName() + '.' + description.getDisplayName());        base.evaluate();      }    };  }}

Then that rule could be applied like so:

import java.util.logging.Logger;import org.example.junit.TestLogger;import org.junit.Rule;import org.junit.Test;public class MyLoggerTest {  @Rule  public final TestLogger logger = new TestLogger();  @Test  public void checkOutMyLogger() {    final Logger log = logger.getLogger();    log.warn("Your test is showing!");  }}

赞赏码

原创粉丝点击