View Source

{section}
{column}

h1. Introduction

Some automatic tests, as integration tests, can require external resources. [Junit|http://junit.org/] provides a mechanism to simplify the management of these external resources in an automatic test context: [Rules|https://github.com/junit-team/junit/wiki/Rules].

The Petals JUnit Framework comes with several external resources ready to use:
* a Petals Registry Overlay,
* a Web server,
* a FTP server,
* an in-memory log handler for Java Util Logging.

Some other external resources are already existing, and are not included in the Petals JUnit Framework. Use them directly:
|| External resource || Solution ||
| Mail servers (SMTP, POP3, IMAP4) | [GreenMail|http://www.icegreen.com/greenmail/] |

{column}
{column:width=35%}
{panel:title=Table of contents}{toc:outline=true}{panel}
{panel:title=Contributors}{contributors:order=name|mode=list|showAnonymous=true|showCount=true|showLastTime=true}{panel}
{column}
{section}

h1. A Petals Registry Overlay as external resource

The {{PetalsRegistryOverlayNode}} rule allows creation of a member of a Petals Registry Overlay cluster that is guaranteed to be deleted when the test method finishes (whether it passes or fails):
{code}
public static class HasPetalsRegistryOverlayCluster {
@Rule
public PetalsRegistryOverlayNode member_1 = new PetalsRegistryOverlayNode();

@Rule
public PetalsRegistryOverlayNode member_2 = new PetalsRegistryOverlayNode();

@Test
public void testUsingPetalsRegistryOverlayCluster() throws IOException {
// ...
final int port_1 = this.member_1.getPort();
// ...
}
}
{code}

h1. A Web server as external resource

The {{WebServer}} rule allows creation of a web-server that is guaranteed to be deleted when the test method finishes (whether it passes or fails):
{code}
public static class HasWebServer {
@Rule
public WebServer webServer = new WebServer();

@Test
public void testUsingWebServer() throws IOException {
// ...
this.webServer.addServlet(servlet, servletPath);
// ...
}
}
{code}

h2. Creating the web-server

The creation of the web-server requires a HTTP port on which it will listen. The default listening port is '{{WebServer.DEFAULT_HTTP_PORT}'. You can use your own HTTP port using the right constructor:
{code}
// A web server listening on the default HTTP port
@Rule
public WebServer webServer = new WebServer();

// A web server listening on your own HTTP port
@Rule
public WebServer webServer = new WebServer(8080);
{code}

h2. Registering servlet

A web-server maps URLs on servlets. If your test requires a servlet, you can register a servlet through the API '{{WebServer.addServlet(httpServlet, path)}}', where '{{httpServlet}}' is your servlet to map on the URL path part '{path}}':
{code}
public static class HasWebServer {
@Rule
public WebServer webServer = new WebServer();

@Test
public void testUsingWebServer() throws IOException {
// ...
this.webServer.addServlet(servlet, servletPath);
// ...
}
}
{code}

h3. Pre-defined servlets

Several servlets are provided into the Petals JUnit Framework:
* {{FileServlet}} that replies with the content of a file,
* {{TextServlet}} that replies with a plain-text content.

h4. Working with {{FileServlet}}

The reply of this servlet is:
* the content-type that has been set when creating the servlet,
* the reply content is the content of the given file when creating the servlet.
{code}
public static class HasWebServer {
@Rule
public WebServer webServer = new WebServer();

@Test
public void testUsingWebServer() throws IOException {
// ...
final AbstractHttpServlet fileServlet = new FileServlet(
new File(Thread.currentThread().getContextClassLoader().getResource("my-file").toURI()),
MimeTypeConstants.APPLICATION_ZIP);
this.webServer.addServlet(fileServlet, fileServlet.getPath());
// ...
}
}
{code}

h4. Working with {{TextServlet}}

The reply of this servlet is:
* the content-type is '{{text/plain}}',
* the reply content is '{{A plain text}}'.
{code}
public static class HasWebServer {
@Rule
public WebServer webServer = new WebServer();

@Test
public void testUsingWebServer() throws IOException {
// ...
final AbstractHttpServlet textServlet = new textServlet();
this.webServer.addServlet(fileServlet, fileServlet.getPath());
// ...
}
}
{code}

h2. Getting the URL of a servlet to invoke

Some facilities was included to build the URL of a servlet to invoke:
* {{WebServer.getHttpBaseUrl()}} : returns the base part of URLs served by the web server,
* {{AbstractHttpServlet.getPath()}} : returns the URL path part of the servlet.
Combining both methods builds easily the URL of the servlet to invoke:
{code}

public static class HasWebServer {
@Rule
public WebServer webServer = new WebServer();

@Test
public void testUsingWebServer() throws IOException {
// ...
final AbstractHttpServlet textServlet = new textServlet();
this.webServer.addServlet(fileServlet, fileServlet.getPath());
// ...
final URL urlToInvoke = new URL(this.webServer.getHttpBaseUrl()
+ textServlet.getPath());
// ...
}
}
{code}

h1. A FTP server as external resource

The {{FTPServer}} rule allows creation of a FTP server that is guaranteed to be deleted when the test method finishes (whether it passes or fails):
{code}
public static class HasFTPServer {
@Rule
public FTPServer ftpServer = new FTPServer();

@Test
public void testUsingFTPServer() throws IOException {
// ...
this.ftpServer.registerUser(user);
// ...
}
}
{code}

h2. Creating the FTP server

The creation of the FTP server requires a FTP port on which it will listen. The default listening port is '{{FTPServer.DEFAULT_HTTP_PORT}'. You can use your own FTP port using the right constructor:
{code}
// A FTP server listening on the default FTP port
@Rule
public FTPServer ftpServer = new FTPServer();

// A FTP server listening on your own FTP port
@Rule
public FTPServer ftpServer = new FTPServer(5000);
{code}

h2. Registering users on FTP server

Users must be declared on the FTP server side to be usable. So you must register the needed users before trying to use the FTP server.

A user is declared in the FTP server using the API '{{FTPServer.registerUser(user)}}':
{code}
public static class HasFTPServer {
@Rule
public FTPServer ftpServer = new FTPServer();

@Test
public void testUsingFTPServer() throws IOException {
// ...
final BaseUser user = new BaseUser();
user.setName("user");
user.setPassword("test");
final List<Authority> authorities = new ArrayList<Authority>();
authorities.add(new WritePermission());
user.setAuthorities(authorities);
final File homeDirectory = new File(this.ftpServer.getRootFileSystem(), user.getName());
user.setHomeDirectory(homeDirectory.getAbsolutePath());
this.ftpServer.registerUser(user);
// ...
}
}
{code}

{tip}If a home directory is set for the user, it will be automatically created if it does not exist.{tip}

h2. Registering files

Use the API '{{registerFile(...)}}' or '{{registerTempFile(...)}} to mock file into a user home directory:
* {{registerFile(User, String)}}: will create a file with the given file name at the root of the user home directory,
* {{registerTempFile(User)}}: will create a file with a random file name at the root of the user home directory,
* {{registerTempFile(User, String)}}: will create a file with a random file name, except for the extension part, at the root of the user home directory,

{code}
public static class HasFTPServer {
@Rule
public FTPServer ftpServer = new FTPServer();

@Test
public void testUsingFTPServer() throws IOException {
// ...
final BaseUser user = new BaseUser();
// ...
this.ftpServer.registerUser(user);

// An empty file with a random name created at the root of a user home directory.
final String aFileOfUser = this.ftpServer.registerTempFile(user);

// An empty file with a random name, except the extension part, created at the root of a user home directory.
final String anotherFileOfUser = this.ftpServer.registerTempFile(user, "xml");

// A non-empty file with a given name created at the root of a user home directory.
final String aFileWithContentOfUser = "my-filename.xml";
assertTrue(this.ftpServer.registerFile(user, aFileWithContentOfUser));
final FileOutputStream fos = new FileOutputStream(new File(user.getHomeDirectory(), aFileWithContentOfUser));
try {
fos.write("<test />".getBytes());
} finally {
fos.close();
}

// ...
}
}
{code}

h1. An in-memory log handler as external resource

The {{InMemoryLogHandler}} rule allows creation of log handler that store log records that are guaranteed to be deleted when the test method finishes (whether it passes or fails):
{code}
public static class HasInMemoryLogHandler {
@Rule
public InMemoryLogHandler inMemoryRealLogHandler = new InMemoryLogHandler();

@Test
public void testUsingInMemoryLogHandler() throws IOException {
// ...
final Logger logger = Logger.getAnonymousLogger();
logger.addHandler(this.inMemoryLogHandler.getHandler());
logger.setLevel(Level.MONIT);
// ...
final List<LogRecord> allLogRecords = this.inMemoryLogHandler.getAllRecords(Level.MONIT);
assertEquals(2, allLogRecords.size());
// ...
}
}
{code}

h2. Clearing log records

The log traces can be cleared using the API '{{InMemoryLogHandler.clear()}}':
{code}
public static class HasInMemoryLogHandler {
@Rule
public InMemoryLogHandler inMemoryRealLogHandler = new InMemoryLogHandler();

@Test
public void testUsingInMemoryLogHandler() throws IOException {
// ...
final Logger logger = Logger.getAnonymousLogger();
logger.addHandler(this.inMemoryLogHandler.getHandler());
logger.setLevel(Level.MONIT);
// ...
final List<LogRecord> allLogRecords = this.inMemoryLogHandler.getAllRecords(Level.MONIT);
assertEquals(2, allLogRecords.size());
// ...
this.inMemoryLogHandler.clear();
assertEquals(0, this.inMemoryLogHandler.getAllRecords().size());
}
}
{code}

h2. Setting the log level of the log handler

You can set the log level of the log handler through the API '{{InMemoryLogHandler.getHandler()}}':
{code}
public static class HasInMemoryLogHandler {
@Rule
public InMemoryLogHandler inMemoryRealLogHandler = new InMemoryLogHandler();

@Test
public void testUsingInMemoryLogHandler() throws IOException {
// ...
this.inMemoryLogHandler.getHandler().setLevel(Level.FINEST);
// ...
}
}
{code}