Rule Them All!
A set of JUnit rules to ease testing setup.
Note dependencies are optional
so you need to bring the ones the rule you use needs.
System properties
There is already some rules to set system properties like http://www.stefan-birkner.de/system-rules/ but it doesn’t support placeholder.
The idea here is to declare system properties through annotations:
@Rule
public final PlaceHoldableSystemProperties unit = new PlaceHoldableSystemProperties();
@Test
@SystemProperties({
@SystemProperty(key = "my.key", value = "my value"),
@SystemProperty(key = "my.other.key", value = "my other value"),
@SystemProperty(key = "other.value.copy", value = "${my.other.key}")
})
public void myTest() throws IOException {
// ...
}
@SystemProperty can be on method and class, both will be merged.
${} uses other system properties and you can also use #{} to match a field. This last case is very useful with arquillian:
@RunAsClient // other you don't have @ArquillianResource URL
@RunWith(Arquillian.class)
public class MyTest {
// @Deployment etc...
@Rule // DON'T FORGET TO PASS THIS AS PARAMETER
public final PlaceHoldableSystemProperties unit = new PlaceHoldableSystemProperties(this);
@ArquillianResource
private URL url;
@Test
@SystemProperties({
@SystemProperty(key = "base.url", value = "#{url}")
})
public void myTest() throws IOException {
// ...
}
}
Ftp
Another rule relies on MockFtpServer library to create a ftp server through a rule.
Here too you define what is your ftp server through annotations. You can define
the port/user/password/root directory to use through @FtpServer
annotation but files too:
@Rule
public final FtpServerRule unit = new FtpServerRule(this);
@Test
@FtpServer(
files = {
@FtpFile(name = "foo.txt", content = "bar.txt")
},
user = "test", password = "pwd"
)
public void myTest() throws IOException {
assertNull(stayNull);
assertNotNull(fs);
assertNotNull(server);
assertTrue(server.getServerControlPort() > 0);
assertEquals(Integer.toString(server.getServerControlPort()), System.getProperty(FtpServer.FTP_PORT));
oldServer = server;
}
Here method OR class annotation is taken into account.
By default port is random and can be retrieved through the FtpServer.FTP_PORT
system property.
If you passed to the rule the test instance (or another class) you can use @UnitInject
to get
the FTP FileSystem
to let the FTP files be dynamic or directly the FakeFtpServer
.
Sftp
This rules allows to mock a very simple SFTP server.
It is mainly inspired from FTP one.
@Rule
public final FtpServerRule unit = new FtpServerRule(this);
@Test
@SftpServer(@SftpFile(name = "/dummy/foo.txt", content = "Awesome"))
public void myTest() throws IOException {
assertNotNull(server);
assertTrue(server.getPort() > 0);
assertEquals(Integer.toString(server.getPort()), System.getProperty(SftpServer.PORT));
oldServer = server;
// client
final JSch jSch = new JSch();
final Session session = jSch.getSession("test", "localhost", server.getPort());
session.setPassword("testpwd");
session.setConfig(new Properties() {{ put("StrictHostKeyChecking", "no"); }});
session.connect();
final ChannelSftp channel = ChannelSftp.class.cast(session.openChannel("sftp"));
channel.connect();
final ByteArrayOutputStream dst = new ByteArrayOutputStream();
channel.get("/dummy/foo.txt", dst);
assertEquals("Awesome", new String(dst.toByteArray()));
}
Note that SftpServer.PORT
system property is set to the server port is you need to use it
with PlaceHoldableSystemProperties
and that you can add later files on the server
using the rule addFile
method.
Spring rule
spring-test
brings a nice runner for JUnit but its lifecycle doesn’t work that well with rules
(in particular if you rely on system properties). To solve it the runner logic was included in SpringRule
:
// @DirtiesContext(...)
@ContextConfiguration("classpath:appCtx.xml")
public class TestSpringRule {
@Rule
public final SpringRule rule = new SpringRule(this);
@Autowired
@Qualifier("foo")
private String fooSpringBean;
@Test
public void run() {
assertEquals("test", fooSpringBean);
}
}
Combine them
Since Java 7 you have no more guarantee about fields order in a class. So when you declare rules you don’t know which one will be called first.
If you use the spring+ftp+system property rules it can be an issue. Let say you use a system property as place holder in your spring context and that it is the ftp url to use. In this case you want to:
-
start the ftp server (and implicitely set the ftp port in system property)
-
set the spring placeholder key in a system property using ftp port system property
-
start spring and reuse the url system property
To sort rules JUnit includes now RuleChain
.
Here a sample spring bean which could be used in this context:
<bean id="port" class="java.lang.Integer">
<constructor-arg value="${ftp.port}" />
</bean>
And here is the test class:
@SystemProperties(@SystemProperty(key = "ftp.port", value = "${" + FtpServer.FTP_PORT + '}'))
@ContextConfiguration("classpath:ftp.xml")
public class TestCombineThemAll {
@Rule
public RuleChain chain = RuleChain.outerRule(new FtpServerRule(this))
.around(new PlaceHoldableSystemProperties())
.around(new SpringRule(this));
@Autowired
@Qualifier("port")
private int port;
@Test
@FtpServer // empty server for the demo
public void run() {
assertTrue(port > 0);
}
}
Hazelcast rule
Simply starts a hazelcast instance either from provided config or from hazelcast.xml:
public class HazelcastRuleTest {
@Rule
public final HazelcastRule rule = new HazelcastRule();
@Test
public void run() {
assertNotNull(rule.getInstance());
assertTrue(rule.getInstance().getLifecycleService().isRunning());
}
}
BaseDir rule
Simple set a system property with current directory, useful to use as placeholder in combination with PlaceHoldableSystemProperties.
Basic usage:
public class BaseDirRuleTest {
@Rule
public final BaseDirRule rule = new BaseDirRule();
@Test
public void ensureSystemPropIsAvailable() {
assertNotNull(System.getProperty(rule.getPropName()));
assertNotNull(System.getProperty("project.basedir"));
}
}
And with PlaceHoldableSystemProperties:
@SystemProperties(@SystemProperty(key = "config.path", value = "${project.basedir}/src/test/configuration"))
public class BaseDirRuleTest {
@Rule
public RuleChain chain = RuleChain.outerRule(new BaseDirRule())
.around(new PlaceHoldableSystemProperties());
// ...
}
PhantomJS rule
Needed dependencies:
<dependency>
<groupId>com.github.detro</groupId>
<artifactId>phantomjsdriver</artifactId>
<version>1.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-phantom-binary</artifactId>
<version>1.9.7</version>
<classifier>linux-64</classifier> <!-- adapt to your case -->
<scope>test</scope>
</dependency>
Usage:
public class PhantomJsRuleTest {
@Rule
public final PhantomJsRule rule = new PhantomJsRule();
@Test
public void run() throws IOException {
rule.getDriver().get("http://localhost:" + port + "/test");
String page = rule.getDriver().getPageSource();
}
}
File
com.github.rmannibucau.rules.api.file.FileRule
is intended to simply the creation of file and the validation
of file output after the test.
Usage:
public class PhantomJsRuleTest {
@Rule
public final FileRule rule = new FileRule();
@Test
@CreateFile(path = "target/in/file.txt", content = "content")
@ExpectedOutput(path = "target/out/file-modified.txt", content = "content 2")
public void run() {
// do something with file.txt
}
}