Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does @Rule do?

Tags:

java

junit

rule

When trying to make a temporary file for unit tests I came across this answer that mentioned "TemporaryFolder JUnit @Rule" and a link explaining how to use it. Which is like this:

@Rule
public TemporaryFolder testFolder = new TemporaryFolder();

and then testFolder.newFile("file.txt")

My question is what does the @Rule annotation do?

Removing the annotation doesn't appear to actually change anything.

like image 262
Aequitas Avatar asked Jul 22 '15 22:07

Aequitas


People also ask

What does JUnit @rule do?

JUnit 4 rules provide a flexible mechanism to enhance tests by running some code around a test case execution. In some sense, it's similar to having @Before and @After annotations in our test class.

What is ClassRule?

Use @ClassRule to set up something that can be reused by all the test methods, if you can achieve that in a static method. Use @Rule to set up something that needs to be created a new, or reset, for each test method.

What is test rule?

A TestRule is an alteration in how a test method, or set of test methods, is run and reported. A TestRule may add additional checks that cause a test that would otherwise fail to pass, or it may perform necessary setup or cleanup for tests, or it may observe test execution to report it elsewhere.

Which of the following can be used to intercept a test method and provide enhancement code for before and after the test execution?

A JUnit 4 rule is a component that intercepts test method calls, and allows us to do something before a test method is run and after a test method has been run. JUnit 4 requires that rule fields are annotated with the @Rule annotation.


1 Answers

As the documentation of Rule and TemporaryFolder states, it takes care of creating a temporary directory before each test method of the respective class and deleting this temporary folder (and its content) after each test method.

You can write your own rules easily by implementing TestRule or MethodRule or extending from any of its implementing classes like ExternalResource.

The same logic could be guaranteed with @Before and @After annotated initialization and cleanup methods. However, you would need to add the logic right into the test-class. If you need the logic in multiple test-classes, you either need to use inheritance, write some arbitrary utility-classes or externalize the behavior. Rules do exactly the latter one by allowing you to re-use this kind of initialization or cleanup-logic and furthermore reduce the code and remove unneeded code so that the focus is on the actual test rather than the configuration of some directories or servers.

This project for example declares two kind of servers (Jetty or Tomcat) you just have to annotate with @Rule in order to use the server for integration or end-to-end tests.

If you want to initialize a rule only once for all test-methods, simply replace @Rule with @ClassRule and initialize the rule as public static. This will initialize the class rule only once and will be reused for each test-method.

@ClassRule
public static JettyServerRule server = new JettyServerRule(new EmbeddedJetty());

@Test
public void myUnitTest() {
    RestTemplate restTemplate = new RestTemplate();
    String url = String.format("http://localhost:%s", server.getPort());

    String response = restTemplate.getForObject(url, String.class);

    assertEquals("Hello World", response);
}

The above sample would initialize a Jetty server only once and each test method of the test-class can reuse this server instead of starting and tearing down a new server for each method.

Multiple rules can even be combined with RuleChain:

@Rule
public RuleChain chain= RuleChain.outerRule(new LoggingRule("outer rule")
                                 .around(new LoggingRule("middle rule")
                                 .around(new LoggingRule("inner rule");

In one of our integration-tests, which sent a couple of requests simultaneously to our JAX-RS service deployed via Restlet on Jetty we have the following rule-definitions:

public static SpringContextRule springContextRule;
public static RestletServerRule restletServer;

static {
    springContextRule = new SpringContextRule(JaxRsRestletTestSpringConfig.class);
    restletServer = new RestletServerRule(springContextRule.getSpringContext());
}

@ClassRule
public static RuleChain ruleChain = RuleChain.outerRule(restletServer).around(springContextRule);

// Tempus Fugit rules
@Rule
public ConcurrentRule concurrently = new ConcurrentRule();

@Rule
public RepeatingRule repeatedly = new RepeatingRule();

Which initializes a spring annotation context before the Restlet/Jetty server is started to enable Spring bean injection. Furthermore, the Tempus Fugit rules are utilized to execute test methods a couple of times concurrently in order to spot race-conditions and concurrency related problems earlier.

like image 82
Roman Vottner Avatar answered Nov 16 '22 05:11

Roman Vottner