In this Arquillian tip we will see how to create an automatic deployment using the new SPI available in Arquillian Core 1.3.0.
By automatic it means we don’t need to declare @Deployment on our test classes. The deployment strategy can be extracted to a common place which will create the deployment archive through the new SPI. We just need to create a class which implements AutomaticDeployment interface:
public class CdiCrudAutomaticDeployment implements AutomaticDeployment { @Override public DeploymentConfiguration generateDeploymentScenario(TestClass tc) { WebArchive war = (WebArchive) EmbeddedMaven.forProject(new File("pom.xml")) .useMaven3Version("3.3.9") .setGoals("package") .setQuiet() .skipTests(true) .ignoreFailure() .build().getDefaultBuiltArchive(); return new DeploymentContentBuilder(war).get(); } }
Note that we’re creating the deployment using EmbeddedMaven as described on ArqTip #2.
Now to activate the AutomaticDeployment we need to register it in a file named org.jboss.arquillian.container.test.spi.client.deployment.AutomaticDeployment under src/test/resources/META-INF/services/ folder. The content of the file is the fully qualified name of the custom AutomaticDeployment implementation.
Now we can remove @Deployment method on our tests:
@RunWith(Arquillian.class) public class AutomaticDeployment { @BeforeDeployment public static Archive beforeDeployment(Archive archive) { //WebArchive war = (WebArchive) archive; WebArchive war = ShrinkWrap.create(WebArchive.class).merge(archive); // use 'merge' to not change original archive war.addAsResource("persistence.xml", "META-INF/persistence.xml");// replace with test persistence (just an example of how to modify the generated deployment, could be done in CdiCrudAutomaticDeployment) return war; } @Inject CarService carService; @Test @UsingDataSet("car.yml") public void shouldCountCars() { assertNotNull(carService); assertEquals(carService.crud().count(), 4); } }
Note that in @BeforeDeployment you can modify the archive generated by AutomaticDeployment.
Skipping the automatic deployment
The AutomaticDeployment will be called for every test class managed by Arquillian. If you have test classes that creates it’s deployment with @Deployment you probably will want to skip the automatic deployments for those tests, following is how you could do that:
public class CdiCrudAutomaticDeployment implements AutomaticDeployment { @Override public DeploymentConfiguration generateDeploymentScenario(TestClass tc) { if (skipAutomaticDeployment(tc)) { return null; // skip if test class has @Deployment on any method } // generate deployment } private boolean skipAutomaticDeployment(TestClass tc) { return tc.getMethod(Deployment.class) != null; } }
Caching the deployment file
As the auto deployment will be called for every test you can cache the deployment archive to avoid building it multiple times:
public class CdiCrudAutomaticDeployment implements AutomaticDeployment { private static WebArchive deploymentCache; @Override public DeploymentConfiguration generateDeploymentScenario(TestClass tc) { if(skipAutomaticDeployment(tc)) { return null; //skip if test class has @Deployment } if(deploymentCache == null) { //avoid rebuild project on every test class deploymentCache = (WebArchive) EmbeddedMaven.forProject(new File("pom.xml")) .useMaven3Version("3.3.9") .setGoals("package") .setQuiet() .skipTests(true) .ignoreFailure() .build().getDefaultBuiltArchive(); } return new DeploymentContentBuilder(ShrinkWrap.create(WebArchive.class).merge(deploymentCache)) //use merge to no to change original archive .get(); } private boolean skipAutomaticDeployment(TestClass tc) { return tc.getMethod(Deployment.class) != null; } }
Change test run mode
Arquillian can run in container (default) or as client (separated JVM), see here for details . This is specified in @Deployment(testable=true/false) but as we don’t have Deployment annotation anymore we can setup this in DeploymentContentBuilder:
return new DeploymentContentBuilder(ShrinkWrap.create(WebArchive.class).merge(deploymentCache))//use merge to not modify original archive .withDeployment().withTestable(isTestable(tc)).build().get();
isTestable() just checks if test class (does not) contains runAsClient annotation:
private boolean isTestable(TestClass tc) { return tc.getAnnotation(RunAsClient.class) == null; }
The complete source code can be found in cdi-crud sample project. The test that uses the automatic deployment can be found here.