Writing Validation Tests with PAX-Exam

This page assumes you are familiar with Java development, Maven and JUnit.

PAX-Exam is a JUnit extension to run JUnit tests in an OSGi environment.
If you are reluctant to write JUnit tests that mimic the DM, you can instead launch a real DM (in its Karaf distribution) and configure it programmatically. Then, you can interact with it with the REST API.

There is an existing Maven module with a Java client for Roboconf’s REST API.
This client does not support all the RESt API yet, but feel free to contribute if necessary.

Notice than there are alternatives to this solution.
They are listed on the other tools page.

Create a Maven Project

Create a new Maven project that will contain your validation tests.
Given the nature of these tests (they need a real deployment target), separating them from the Roboconf project itself is better.

Use the following POM as a basis.
It imports the required dependencies.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
		xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

	<modelVersion>4.0.0</modelVersion>

	<groupId>net.roboconf</groupId>
	<artifactId>validation-project-sample-pax</artifactId>
	<name>Roboconf :: Validation Project Sample with PAX-Exam</name>
	<version>1.0-SNAPSHOT</version>
	
	<properties>
		<roboconf.version>0.7</roboconf.version>
		<pax.exam.version>4.7.0</pax.exam.version>
		<pax.url.aether.version>2.4.1</pax.url.aether.version>
	</properties>

	<dependencies>

		<dependency>
			<groupId>net.roboconf</groupId>
			<artifactId>roboconf-core</artifactId>
			<version>${roboconf.version}</version>
		</dependency>
		
		<dependency>
			<groupId>net.roboconf</groupId>
			<artifactId>roboconf-dm-rest-commons</artifactId>
			<version>${roboconf.version}</version>
		</dependency>

		<dependency>
			<groupId>net.roboconf</groupId>
			<artifactId>roboconf-dm-rest-client</artifactId>
			<version>${roboconf.version}</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
			<version>4.12</version>
		</dependency>
		
		<!-- Dependencies for pax exam karaf container -->
		
		<dependency>
			<groupId>org.ops4j.pax.exam</groupId>
			<artifactId>pax-exam-container-karaf</artifactId>
			<version>${pax.exam.version}</version>
			<scope>test</scope>
		</dependency>
		
		<dependency>
			<groupId>org.ops4j.pax.exam</groupId>
			<artifactId>pax-exam-junit4</artifactId>
			<version>${pax.exam.version}</version>
			<scope>test</scope>
		</dependency>
		
		<dependency>
			<groupId>org.ops4j.pax.exam</groupId>
			<artifactId>pax-exam</artifactId>
			<version>${pax.exam.version}</version>
			<scope>test</scope>
		</dependency>
		
		<dependency>
			<groupId>org.ops4j.pax.url</groupId>
			<artifactId>pax-url-aether</artifactId>
			<version>${pax.url.aether.version}</version>
			<scope>test</scope>
		</dependency>
		
		<dependency>
			<groupId>javax.inject</groupId>
			<artifactId>javax.inject</artifactId>
			<version>1</version>
			<scope>test</scope>
		</dependency>
		
	</dependencies>
</project>

Then, create a src/test/java directory.

Test Sample

A commented code sample seems more appropriated than some bla-bla.
We could have created a Gist too, but this sample may evolve with versions.

package net.roboconf.sample;

import static org.ops4j.pax.exam.CoreOptions.cleanCaches;
import static org.ops4j.pax.exam.CoreOptions.maven;
import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFilePut;
import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.karafDistributionConfiguration;
import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.keepRuntimeFolder;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import net.roboconf.core.Constants;
import net.roboconf.core.model.beans.Application;
import net.roboconf.core.model.beans.ApplicationTemplate;
import net.roboconf.dm.rest.client.WsClient;

import org.junit.Assert;
import org.junit.Test;
import org.ops4j.pax.exam.ExamSystem;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.TestContainer;
import org.ops4j.pax.exam.karaf.container.internal.KarafTestContainer;
import org.ops4j.pax.exam.options.MavenArtifactUrlReference;
import org.ops4j.pax.exam.spi.PaxExamRuntime;

public class SampleTest {

	private static final String ROOT_URL = "http://localhost:8181/roboconf-dm";


	/**
	 * @return the Karaf configuration (OSGi environment configuration)
	 */
	public Option[] config() {

		MavenArtifactUrlReference karafUrl = maven()
				.groupId( "net.roboconf" )
				.artifactId( "roboconf-karaf-dist-dm" )
				.version( "0.7-SNAPSHOT" )
				.type( "tar.gz" );

		// Configure the platform
		List<Option> options = new ArrayList<Option> ();
		options.add( karafDistributionConfiguration()
				.frameworkUrl( karafUrl )
				.unpackDirectory( new File( "target/pax-exam" ))
				.useDeployFolder( false ));

		options.add( cleanCaches( true ));
		options.add( keepRuntimeFolder());

		// Which messaging configuration? Example with RabbitMQ.
		options.add( editConfigurationFilePut(
				"etc/net.roboconf.dm.configuration.cfg",
				Constants.MESSAGING_TYPE,
				"rabbitmq" ));

		return options.toArray( new Option[ options.size()]);
	}


	@Test
	public void runScenario() throws Exception {

		File appDirectory = new File( "my-app-directory" );

		// Prepare to run an agent distribution
		Option[] options = config();
		ExamSystem system = PaxExamRuntime.createServerSystem( options );
		TestContainer container = PaxExamRuntime.createContainer( system );
		Assert.assertEquals( KarafTestContainer.class, container.getClass());

		WsClient client = null;
		try {
			// Start the DM's distribution... and wait...
			container.start();

			// You may to poll for REST services...
			Thread.sleep( 5000 );

			// Build a REST client
			client = new WsClient( ROOT_URL );

			// Perform the checks
			testRestInteractions( appDirectory.getAbsolutePath(), client );

		} finally {
			container.stop();
			if( client != null )
				client.destroy();
		}
	}


	private void testRestInteractions( String appLocation, WsClient client  )
	throws Exception {

		// Load an application template
		Assert.assertEquals( 0, client.getManagementDelegate().listApplicationTemplates().size());
		client.getManagementDelegate().loadApplicationTemplate( appLocation );
		List<ApplicationTemplate> templates = client.getManagementDelegate().listApplicationTemplates();
		Assert.assertEquals( 1, templates.size());

		ApplicationTemplate tpl = templates.get( 0 );
		Assert.assertEquals( "Legacy LAMP", tpl.getName());
		Assert.assertEquals( "sample", tpl.getQualifier());

		// Create an application
		Assert.assertEquals( 0, client.getManagementDelegate().listApplications().size());
		client.getManagementDelegate().createApplication( "app1", tpl.getName(), tpl.getQualifier());
		List<Application> apps = client.getManagementDelegate().listApplications();
		Assert.assertEquals( 1, apps.size());

		Application receivedApp = apps.get( 0 );
		Assert.assertEquals( "app1", receivedApp.getName());
		Assert.assertEquals( "Legacy LAMP", receivedApp.getTemplate().getName());
		Assert.assertEquals( "sample", receivedApp.getTemplate().getQualifier());

		// Etc.
	}
}