Testing your application with Apache DeltaSpike CdiCtrl

About CdiCtrl

CdiCtrl is not part of Apache OpenWebBeans but a module of Apache DeltaSpike.

The CdiCtrl interface abstracts away all the logic to boot a CDI Container and controls the lifecycle of it's Contexts (Request Context, Session Context, etc).

The actual CDI Container is determined by using the java.util.ServiceLoader. There are a few different implementations available. Besides Apache OpenWebBeans there are also plugins for JBoss Weld and Apache TomEE.

Adding OpenWebBeans CdiCtrl to your project

The following are the dependencies you need in your Apache Maven pom.xml file in addition to OWB itself:

<dependency>
    <groupId>org.apache.deltaspike.cdictrl</groupId>
    <artifactId>deltaspike-cdictrl-api</artifactId>
    <version>$  {deltaspike.version}</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.apache.deltaspike.cdictrl</groupId>
    <artifactId>deltaspike-cdictrl-owb</artifactId>
    <version>$  {deltaspike.version}</version>
    <scope>test</scope>
</dependency>

Why use CdiCtrl for your unit tests?

Whenever you need to write unit tests for a full application, then you will need to have a CDI container scann all your classes, create Bean<T> from it and provide them for injection. All this can be done by either using JUnits @RunWith or by simply creating a common base class for your unit tests which boots up the container on your test classpath.

There is no need to restart the container for each and every of your unit tests as this would cause a big performance loss. Instead it is usually sufficient to use the CdiCtrls ContextControl mechanism to just stop and restart the respective CDI Contexts.

Such a base class could look roughly like the following:

import org.apache.deltaspike.cdise.api.CdiContainer;
import org.apache.deltaspike.cdise.api.CdiContainerLoader;
import org.apache.deltaspike.core.api.projectstage.ProjectStage;
import org.apache.deltaspike.core.util.ProjectStageProducer;
import org.apache.deltaspike.core.api.provider.BeanProvider;

public abstract class ContainerTest  {

    protected static volatile CdiContainer cdiContainer;
    // nice to know, since testng executes tests in parallel.
    protected static int containerRefCount = 0;

    protected ProjectStage runInProjectStage()  {
        return ProjectStage.UnitTest;
    }
    
    /**
     * Starts container
     * @throws Exception in case of severe problem
     */
    @BeforeMethod
    public final void beforeMethod() throws Exception  {
        containerRefCount++;

        if (cdiContainer == null)  {
            // setting up the Apache DeltaSpike ProjectStage
            ProjectStage projectStage = runInProjectStage();
            ProjectStageProducer.setProjectStage(projectStage);

            cdiContainer = CdiContainerLoader.getCdiContainer();

            cdiContainer.boot();
            cdiContainer.getContextControl().startContexts();
        }
        else  {
            cleanInstances();
        }
    }


    public static CdiContainer getCdiContainer()  {
        return cdiContainer;
    }

    /**
     * This will fill all the InjectionPoints of the current test class for you
     */
    @BeforeClass
    public final void beforeClass() throws Exception  {
        beforeMethod();

        // perform injection into the very own test class
        BeanManager beanManager = cdiContainer.getBeanManager();

        CreationalContext creationalContext = beanManager.createCreationalContext(null);

        AnnotatedType annotatedType = beanManager.createAnnotatedType(this.getClass());
        InjectionTarget injectionTarget = beanManager.createInjectionTarget(annotatedType);
        injectionTarget.inject(this, creationalContext);

        // this is a trick we use to have proper DB transactions when using the entitymanager-per-request pattern
        cleanInstances();
        cleanUpDb();
        cleanInstances();
    }

    /**
     * Shuts down container.
     * @throws Exception in case of severe problem
     */
    @AfterMethod
    public final void afterMethod() throws Exception  {
        if (cdiContainer != null)  {
            cleanInstances();
            containerRefCount--;
        }
    }

    /**
     * clean the NormalScoped contextual instances by stopping and restarting
     * some contexts. You could also restart the ApplicationScoped context
     * if you have some caches in your classes. 
     */
    public final void cleanInstances() throws Exception  {
        cdiContainer.getContextControl().stopContext(RequestScoped.class);
        cdiContainer.getContextControl().startContext(RequestScoped.class);
        cdiContainer.getContextControl().stopContext(SessionScoped.class);
        cdiContainer.getContextControl().startContext(SessionScoped.class);
    }

    @AfterSuite
    public synchronized void shutdownContainer() throws Exception  {
        if (cdiContainer != null)  {
            cdiContainer.shutdown();
            cdiContainer = null;
        }
    }

    public void finalize() throws Throwable  {
        shutdownContainer();
        super.finalize();
    }


    /**
     * Override this method for database clean up.
     *
     * @throws Exception in case of severe problem
     */
    protected void cleanUpDb() throws Exception  {
        //Override in subclasses when needed
    }

    protected <T> T getInstance(Class<T> type, Qualifier... qualifiers)  {
        return BeanProvider.getContextualReference(type, qualifiers);
    }

}

Testing JavaEE applications

You can also plug in a cdictrl backend for Apache TomEE whenever you need to not only test CDI applications but a full JavaEE application which has EJBs, managed DataSources, JTA, etc The only thing you need to do is to replace your deltaspike-cdictrl-owb dependency in your pom with deltaspike-cdictrl-openejb. Since Apache TomEE and Apache OpenEJB both contain OpenWebBeans as CDI container you will get all the OWB functionality plus other JavaEE functionality.

You can pass DataSource configuration by simply providing a Properties instance to CdiContainer.boot(dbConfiguration) in the beforeMethod method of the test class above:

public final void beforeMethod() throws Exception  {
    containerRefCount++;

    if (cdiContainer == null)  {
        // setting up the Apache DeltaSpike ProjectStage
        ProjectStage projectStage = runInProjectStage();
        ProjectStageProducer.setProjectStage(projectStage);
        cdiContainer = CdiContainerLoader.getCdiContainer();

        Properties dbProperties = new Properties();
        String dbvendor = ConfigResolver.getPropertyValue("dbvendor", "h2");
        URL dbPropertiesUrl =  getClass().getResource("/db/db-" + dbvendor + ".properties");
        if (dbPropertiesUrl != null)  {
            InputStream is = dbPropertiesUrl.openStream();
            try  {
                dbProperties.load(is);
            }
            finally  {
                is.close();
            }
        }

        cdiContainer.boot(dbProperties);
    }
    else  {
        cleanInstances();
    }
}

The db/db-mysql.properties file for Apache OpenEJB (the former name of TomEE) would look like:

MYDS = new://Resource?type=DataSource
MYDS.JdbcDriver = org.h2.Driver
MYDS.JdbcUrl = jdbc:h2:file:/tmp/h2/myappdb
MYDS.JtaManaged = true
MYDS.UserName = sa
MYDS.Password =