This reduces the amount of duplicated code and means that if the UI changes, the fix need only be applied in one place.
You can think about Container Object as areas of a container (for now Docker container) that your test might interact with. For example some of these areas could be:
- To get the host IP where container is running.
- The bounded port for a given exposed port.
- Any parameter configured inside the configuration file (Dockerfile) like a user or password to access to the service which the container exposes.
- Definition of the containers.
One way to do this is using Docker to start a FTP server just before executing the test, then execute the test using this Docker container for FTP server, before stopping the container check that the file is there, and finally stop the container.
- Which image is used
- IP and bounded port of host where this FTP server is running
- Username and password to access to the FTP server
- Methods for asserting the existence of a file
Again as in Page Object, any change on the container only affects the Container Object and not the test itself.
The Container Object will be like a simple POJO with special annotations:
- @Cube annotation configures Container Object.
- A Container Object can be enriched with Arquillian enrichers.
- Bounded port is injected for given exposed port.
- Container Object hides how to connect to PingPong server.
Notice that this can be an array to set more than one port binding definition.
Next annotation is @CubeDockerFile which configure how Container is created. In this case using a Dockerfile located at default classpath location. The default location is the package+classname, so for example in previous case, Dockerfile should be placed at org/superbiz/containerobject/PingPongContainer directory.
Of course you can set any other class path location by passing as value of the annotation. CubeDockerFile annotation sets the location where the Dockerfile is found and not the file itself.
Also this location should be reachable from ClassLoader, so it means it should be loaded from classpath in order to find it.
Any Cube can be enriched with any client side enricher, in this case with @HostIp enricher, but it could be enriched with DockerClient using @ArquillianResource as well.
Finally the @HostPort is used to translate the exposed port to bound port.
So in this example port value will be 5000. You are going to learn briefly why this annotation is important.
And then you can start using this container object in your test:
It is very important to annotate the field with Cube, so before Arquillian runs the test, it can detect that it needs to start a new Cube (Docker container), create the Container Object and inject it in the test.
Notice that this annotation is exactly the same as used when you defined the Container Object.
And it is in this way because you can override any property of the Container Object from the test side. This is why @HostPort annotation is important, since port can be changed from the test definition, you need to find a way to inject the correct port inside the container object.
In this post I have introduced Container Object pattern and how can be used in Arquillian Cube. But this is only an small taste, you can read more about Arquillian Cube and Container Object integration at https://github.com/arquillian/arquillian-cube#arquillian-cube-and-container-object
Also a running examples can be found at https://github.com/arquillian/arquillian-cube/tree/master/docker/ftest-docker-containerobject
We keep learning,
It's time to see what I can do, To test the limits and break through, No right, no wrong, no rules for me, I'm free! (Let It Go - Idina Menzel)