Arquillian automatic deployment (arqtip #3)

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.

References:

Leave a comment