Rules
规则允许在测试类中对每个测试方法的行为进行非常灵活的添加或重新定义。测试人员可以重用或扩展所提供的规则之一,或者编写自己的规则。
Example
对于一个规则使用的例子,可以使用临时文件夹和预期功能来进行测试:
public class DigitalAssetManagerTest {
@Rule
public final TemporaryFolder tempFolder = new TemporaryFolder();
@Rule
public final ExpectedException exception = ExpectedException.none();
@Test
public void countsAssets() throws IOException {
File icon = tempFolder.newFile("icon.png");
File assets = tempFolder.newFolder("assets");
createAssets(assets, 3);
DigitalAssetManager dam = new DigitalAssetManager(icon, assets);
assertEquals(3, dam.getAssetCount());
}
private void createAssets(File assets, int numberOfAssets) throws IOException {
for (int index = 0; index < numberOfAssets; index++) {
File asset = new File(assets, String.format("asset-%d.mpg", index));
Assert.assertTrue("Asset couldn't be created.", asset.createNewFile());
}
}
@Test
public void throwsIllegalArgumentExceptionIfIconIsNull() {
exception.expect(IllegalArgumentException.class);
exception.expectMessage("Icon is null, not a file, or doesn't exist.");
new DigitalAssetManager(null, null);
}
}
在分配中提供的基本规则
TemporaryFolder Rule(临时文件存放位置 规则)
TemporaryFolder Rule允许的临时文件夹的文件和文件夹,删除时,测试方法完成创作(无论通过或失败)。默认情况下,如果资源不能删除,则不会引发异常:
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");
// ...
}
}
ExternalResource Rules
ExternalResource 是一个基于Rule 的类,他能在测试开始之前设置一些资源,其实就是类似@Before , 不过就是换了Rule来实现而已。
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);
}
}
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"));
}
}
Verifier Rule
Verifier Rule像Errorcollector基类,可以将原本通过测试方法测试失败如果验证检查失败。
private static String sequence;
public static class UsesVerifier {
@Rule
public final Verifier collector = new Verifier() {
@Override
protected void verify() {
sequence += "verify ";
}
};
@Test
public void example() {
sequence += "test ";
}
@Test
public void verifierRunsAfterTest() {
sequence = "";
assertThat(testResult(UsesVerifier.class), isSuccessful());
assertEquals("test verify ", sequence);
}
}
TestWatchman/TestWatcher Rules
- TestWatcher replaces TestWatchman from version 4.9. It implements TestRule, not MethodRule -- http://junit.org/javadoc/latest/org/junit/rules/TestWatcher.html
- TestWatchman was introduced in JUnit 4.7, it uses a MethodRule, which is now deprecated. -- http://junit.org/javadoc/latest/org/junit/rules/TestWatchman.html
- 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:
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() {
}
}
TestName Rule
The TestName Rule makes the current test name available inside test methods:
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());
}
}
Timeout Rule
The Timeout Rule applies the same timeout to all test methods in a class:
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(;;) {}
}
}
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);
}
}
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!");
}
}