Blog

Thursday, 19 April 2012

Mock contexts for Arquillian

Do you have @ViewScoped or @ConversationScoped annotated beans that you want to test with Arquillian?

Do you get "WELD-001303 No active contexts for scope type javax.enterprise.context.ConversationScoped"?

Well now you get mock-contexts-extension to the rescue.

Arquillian 1.0.0.Final enables only Application, Session and Request contexts.
Our seam based applications mostly use ViewScoped and ConversationScoped components so we need those contexts to be active during tests. For that I've written Arquillian extension that activates mock contexts.

Let's take a look at such test:
package pl.com.it_crowd.arquillian.mock_contexts.test;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import pl.com.it_crowd.arquillian.mock_contexts.ConversationScopeRequired;
import pl.com.it_crowd.arquillian.mock_contexts.ViewScopeRequired;

import javax.inject.Inject;

@RunWith(Arquillian.class)
public class SampleTest {
// ------------------------------ FIELDS ------------------------------

    @Inject
    private ConversationalComponent conversationalComponent;

    @Inject
    private ViewScopedComponent viewScopedComponent;

// -------------------------- STATIC METHODS --------------------------

    @Deployment
    public static Archive getArchive()
    {
        return ShrinkWrap.create(WebArchive.class, SampleTest.class.getSimpleName() + ".war")
            .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
            .addClass(ConversationalComponent.class)
            .addClass(ViewScopedComponent.class);
    }

// -------------------------- OTHER METHODS --------------------------

    @ConversationScopeRequired
    @Test
    public void conversationScopedBeanTest()
    {
        Assert.assertEquals(0, conversationalComponent.getIndex());
        conversationalComponent.setIndex(1);
        Assert.assertEquals(1, conversationalComponent.getIndex());
    }

    @ViewScopeRequired
    @Test
    public void viewScopedBeanTest()
    {
        Assert.assertEquals(0, viewScopedComponent.getIndex());
        viewScopedComponent.setIndex(1);
        Assert.assertEquals(1, viewScopedComponent.getIndex());
    }
}

We've got 2 tests, one testing bean that is annotated with @ConversationScoped and one testing bean with @ViewScoped annotation.

The only additional thing here are @ConversationScopeRequired and @ViewScopeRequired annotations on test methods. They are from extension's api module.

Before each test the extension will check if any conversation context is active and if not it will activate mock conversation context. The same applies for view scoped context.

We only need to add following dependencies to the POM:
        <dependency>
            <groupid>pl.com.it-crowd.mock-contexts-extension</groupid>
            <artifactid>mock-contexts-extension-api</artifactid>
            <version>0.1-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupid>pl.com.it-crowd.mock-contexts-extension</groupid>
            <artifactid>mock-contexts-extension-impl</artifactid>
            <version>0.1-SNAPSHOT</version>
            <scope>runtime</scope>
        </dependency> 
And one other thing. This extension requires you to add following line to arquillian.xml:
        <defaultprotocol type="Servlet 3.0"/>
Currently the extension is not part of official Arquillian project but let's hope Aslak will accept it.

If you're interested check out the code at https://github.com/blabno/mock-contexts-extension.

Here are our repo coords:
            <repositories>
                <repository>
                    <id>it-crowd.com.pl</id>
                    <url>http://artifactory.it-crowd.com.pl/repo</url>
                    <snapshots>
                        <enabled>true</enabled>
                    </snapshots>
                </repository>
            </repositories>
            <pluginrepositories>
                <pluginrepository>
                    <id>it-crowd.com.pl</id>
                    <url>http://artifactory.it-crowd.com.pl/repo</url>
                    <snapshots>
                        <enabled>true</enabled>
                    </snapshots>
                </pluginrepository>
            </pluginrepositories>