Arquillian Cube. Let’s zap ALL these bugs, even the infrastructure ones.
Docker is becoming the de-facto project for deploying applications inside lightweight software containers in isolation. Because they are really lightweight they are perfect not only to use in production, but to be used inside developer/qa/CI machine. So the natural step is start writing tests of your software that runs against these containers.
In fact if you are running Docker on production, you can start writing infrastructure tests and run them as a process of your release chain (even debugging in developer machine) before touching production.
That’s pretty cool but you need a way to automate all these steps from the point of view of developer. It should be amazing if we could startup Docker containers, deploy there the project and execute the tests from a JUnit and as easy as a simple JUnit. And this is what Arquillian Cube does.
Arquillian Cube is an Arquillian extension that can be used to manager Docker containers from Arquillian.
Extension is named Cube for two reasons:
- Because Docker is like a cube
- Because Borg starship is named cube and well because we are moving tests close to production we can say that “any resistance is futile, bugs will be assimilated”.
With this extension you can start a Docker container with a server installed, deploy the required deployable file within it and execute Arquillian tests.
The key point here is that if Docker is used as deployable platform in production, your tests are executed in a the same container as it will be in production, so your tests are even more real than before.
But also let you start a container with every required service like database, mail server, … and instead of stubbing or using fake objects your tests can use real servers.
Let’s see a really simple example which shows you how powerful and easy to use Arquillian Cube is. Keep in mind that this extension can be used along with other Arquillian extensions like Arquillian Drone/Graphene to run functional tests.
Arquillian Cube relies on docker-java API.
To use Arquillian Cube you need a Docker daemon running on a computer (it can be local or not), but probably it will be at local.
By default Docker server is using UNIX sockets for communication with the Docker client, however docker-java client uses TCP/IP to connect to the Docker server, so you will need to make sure that your Docker server is listening on TCP port. To allow Docker server to use TCP add the following line to /etc/default/docker:
DOCKER_OPTS=”-H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock”
After restarting the Docker daemon you need to make sure that Docker is up and listening on TCP.
$ docker -H tcp://127.0.0.1:2375 version
Client version: 0.8.0
Go version (client): go1.2
Git commit (client): cc3a8c8
Server version: 1.2.0
Git commit (server): fa7b24f
Go version (server): go1.3.1
If you cannot see the client and server versions then means that something is wrong in Docker installation.
After having a Docker server installed we can start using Arquillian Cube.
In this case we are going to use a Docker image with an Apache Tomcat and we are going to test a Servlet.
@WebServlet("/HelloWorld") public class HelloWorldServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter writer = resp.getWriter(); writer.println("Hello World"); } }
And the test:
@RunWith(Arquillian.class) public class HelloWorldServletTest { @Deployment(testable=false) public static WebArchive create() { return ShrinkWrap.create(WebArchive.class, "hello.war").addClass(HelloWorldServlet.class); } @Test public void should_parse_and_load_configuration_file(@ArquillianResource URL resource) throws IOException { URL obj = new URL(resource, "HelloWorld"); HttpURLConnection con = (HttpURLConnection) obj.openConnection(); con.setRequestMethod("GET"); BufferedReader in = new BufferedReader( new InputStreamReader(con.getInputStream())); String inputLine; StringBuffer response = new StringBuffer(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } in.close(); assertThat(response.toString(), is("Hello World")); } }
Notice that the test looks like any other Arquillian test.
Next step is add Arquillian dependencies as described in http://arquillian.org/guides/getting_started and add arquillian-cube-docker dependency:
<dependency> <groupId>org.arquillian.cube</groupId> <artifactId>arquillian-cube-docker</artifactId> <version>1.0.0.Alpha1</version> <scope>test</scope> </dependency>
Because we are using Tomcat and because it is being executed in a remote host (in fact this is true because Tomcat is running inside Docker which is external to Arquillian), we need to add Tomcat remote adapter.
<dependency> <groupId>org.jboss.arquillian.container</groupId> <artifactId>arquillian-tomcat-remote-7</artifactId> <version>1.0.0.CR7</version> <scope>test</scope> </dependency>
And finally arquillian.xml is configured:
<?xml version="1.0"?> <arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://jboss.org/schema/arquillian" xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd"> <extension qualifier="docker"> (1) <property name="serverVersion">1.12</property> (2) <property name="serverUri">http://localhost:2375</property> (3) <property name="dockerContainers"> (4) tomcat: image: tutum/tomcat:7.0 exposedPorts: [8089/tcp] await: strategy: polling env: [TOMCAT_PASS=mypass, JAVA_OPTS=-Dcom.sun.management.jmxremote.port=8089 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false] portBindings: - exposedPort: 8089/tcp port: 8089 - exposedPort: 8080/tcp port: 8080 </property> </extension> <container qualifier="tomcat" default="true"> (5) <configuration> <property name="host">localhost</property> (6) <property name="httpPort">8080</property> (7) <property name="user">admin</property> (8) <property name="pass">mypass</property> </configuration> </container> </arquillian>
(1) Arquillian Cube extension is registered.
(2) Docker server version is required.
(3) Docker server URI is required. In case you are using a remote Docker host or Boot2Docker here you need to set the remote host ip, but in this case Docker server is on same machine.
(4) A Docker container contains a lot of parameters that can be configured. To avoid having to create one XML property for each one, a YAML content can be embedded directly as property.
(5) Configuration of Tomcat remote adapter. Cube will start the Docker container when it is ran in the same context as an Arquillian container with the same name.
(6) Host can be localhost because there is a port forwarding between container and Docker server.
(7) Port is exposed as well.
(8) User and password are required to deploy the war file to remote Tomcat.
And that’s all. Now you can run your test and you will see how tutum/tomcat:7.0 image is downloaded and started. Then test is executed and finally the docker container is stopped.
This has been a simple example, but you can do a lot of more things like creating images from Dockerfile, orchestrate several docker images or enrich the test to programmatically manipulate containers.
For more information you can read https://github.com/arquillian/arquillian-cube and of course any feedback will be more than welcomed.
Reference: | Arquillian Cube. Let’s zap ALL these bugs, even the infrastructure ones. from our JCG partner Alex Soto at the One Jar To Rule Them All blog. |