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>

26 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Hello, nice job with this extensions, I hope Aslak accept it too.
    I'm trying to use your ViewScopedRequired but when your method MockViewScopeCDIExtension.addScope()
    is called, on line 30 I got an NoClassDefFoundError for javax.faces.bean.ViewScoped.

    Maybe the problem is classpath for your extension, I saw your pom and it has this jsf-api and cdi dependency, I'm trying to figure out what's happening, do you have any ideas?

    I've tried to put the jsf-api, cdi, etc. dependencies, in my test classpath, but nothing works, always the same Error.

    ReplyDelete
  3. Here's the stacktrace:

    Caused by: java.lang.NoClassDefFoundError: javax/faces/bean/ViewScoped
    at pl.com.it_crowd.arquillian.mock_contexts.container.MockViewScopeCDIExtension.addScope(MockViewScopeCDIExtension.java:30)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.jboss.weld.util.reflection.SecureReflections$13.work(SecureReflections.java:264)
    at org.jboss.weld.util.reflection.SecureReflectionAccess.run(SecureReflectionAccess.java:52)
    at org.jboss.weld.util.reflection.SecureReflectionAccess.runAsInvocation(SecureReflectionAccess.java:137)
    at org.jboss.weld.util.reflection.SecureReflections.invoke(SecureReflections.java:260)
    at org.jboss.weld.introspector.jlr.WeldMethodImpl.invokeOnInstance(WeldMethodImpl.java:170)
    at org.jboss.weld.introspector.ForwardingWeldMethod.invokeOnInstance(ForwardingWeldMethod.java:51)
    at org.jboss.weld.injection.MethodInjectionPoint.invokeOnInstanceWithSpecialValue(MethodInjectionPoint.java:154)
    ... 14 more
    Caused by: java.lang.ClassNotFoundException: javax.faces.bean.ViewScoped from [Module \"deployment.arquillian-service:main\" from Service Module Loader]
    at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:190)

    thank you

    ReplyDelete
    Replies
    1. Are JSF api packaged in your test archive?

      Delete
  4. Are JSF api packaged in your test archive?

    ReplyDelete
  5. https://community.jboss.org/thread/201854

    ReplyDelete
  6. This comment has been removed by the author.

    ReplyDelete
  7. Hi Bernard.

    Is there a way to handle this boring error when deploying web archive to JBoss AS 7.1?

    Caused by: java.lang.NoClassDefFoundError: javax/faces/bean/ViewScoped
    at pl.com.it_crowd.arquillian.mock_contexts.container.MockViewScopeCDIExtension.addScope(MockViewScopeCDIExtension.java:30)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.jboss.weld.util.reflection.SecureReflections$13.work(SecureReflections.java:264)
    at org.jboss.weld.util.reflection.SecureReflectionAccess.run(SecureReflectionAccess.java:52)
    at org.jboss.weld.util.reflection.SecureReflectionAccess.runAsInvocation(SecureReflectionAccess.java:137)
    at org.jboss.weld.util.reflection.SecureReflections.invoke(SecureReflections.java:260)
    at org.jboss.weld.introspector.jlr.WeldMethodImpl.invokeOnInstance(WeldMethodImpl.java:170)
    at org.jboss.weld.introspector.ForwardingWeldMethod.invokeOnInstance(ForwardingWeldMethod.java:51)
    at org.jboss.weld.injection.MethodInjectionPoint.invokeOnInstanceWithSpecialValue(MethodInjectionPoint.java:154)
    ... 14 more
    Caused by: java.lang.ClassNotFoundException: javax.faces.bean.ViewScoped from [Module \"deployment.arquillian-service:main\" from Service Module Loader]
    at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:190)

    No, JSF api is not in my test archive. Assuming it is a dependency provided by the container, right?

    ReplyDelete
  8. Hi Bernard.

    Is there a way to handle with this boring error when deploying web archives to JBoss AS 7.1?

    Caused by: java.lang.NoClassDefFoundError: javax/faces/bean/ViewScoped
    at pl.com.it_crowd.arquillian.mock_contexts.container.MockViewScopeCDIExtension.addScope(MockViewScopeCDIExtension.java:30)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.jboss.weld.util.reflection.SecureReflections$13.work(SecureReflections.java:264)
    at org.jboss.weld.util.reflection.SecureReflectionAccess.run(SecureReflectionAccess.java:52)
    at org.jboss.weld.util.reflection.SecureReflectionAccess.runAsInvocation(SecureReflectionAccess.java:137)
    at org.jboss.weld.util.reflection.SecureReflections.invoke(SecureReflections.java:260)
    at org.jboss.weld.introspector.jlr.WeldMethodImpl.invokeOnInstance(WeldMethodImpl.java:170)
    at org.jboss.weld.introspector.ForwardingWeldMethod.invokeOnInstance(ForwardingWeldMethod.java:51)
    at org.jboss.weld.injection.MethodInjectionPoint.invokeOnInstanceWithSpecialValue(MethodInjectionPoint.java:154)
    ... 14 more
    Caused by: java.lang.ClassNotFoundException: javax.faces.bean.ViewScoped from [Module \"deployment.arquillian-service:main\" from Service Module Loader]
    at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:190)


    No, JSF API is not present in my test archive. Assuming it is a dependency provided by the container, right?

    Thanks.

    ReplyDelete
  9. This is great, but it seems to blow up when used in an EnterpeiseArchive context. Stack trace:

    java.lang.UnsupportedOperationException: EnterpriseArchive does not support classes
    at org.jboss.shrinkwrap.impl.base.spec.EnterpriseArchiveImpl.getClassesPath(EnterpriseArchiveImpl.java:142)
    at org.jboss.shrinkwrap.impl.base.container.ContainerBase.addClasses(ContainerBase.java:1234)
    at org.jboss.shrinkwrap.impl.base.container.ContainerBase.addClass(ContainerBase.java:1170)
    at pl.com.it_crowd.arquillian.mock_contexts.client.MockContextsProcessor.process(MockContextsProcessor.java:23)

    ReplyDelete
    Replies
    1. Could you provide sample testcase? Or at least test class where you create package for Arquillian?

      Delete
    2. I've fixed this issue. Try getting fresh snapshot from our repo.

      Delete
    3. Would that latest be in the Maven repo? Also, how can I attach a zip for you (if you still need one that is)?

      Delete
    4. Yes, latest snapshot is in maven (it's still 0.1-SNAPSHOT).
      You can publish it on github, on google docs or send it over to our company contact mail.

      Delete
  10. E-mail sent to
    contact@it-crowd.com.pl

    Subject:
    Re BLOG POST Bernard Łabno 11 September 2012 10:28

    From
    john@memoriesdreamsandreflections.org

    ReplyDelete
  11. Bernard,

    I just want to say thanks for throwing this together. I would love to see this be part of the Arquillian core. Anybody who is using Seam managed persistence contexts (like below) will need to use this in order to run Arquillian tests against the entity manager.

    @ExtensionManaged
    @Produces
    @PersistenceUnit(unitName="myAppEm")
    @ConversationScoped
    EntityManagerFactory myEntityManagerFactory;

    Ken

    ReplyDelete
  12. This comment has been removed by the author.

    ReplyDelete
  13. Hi Bernard, this doesn't work for me as I run into a CNF of org.jboss.shrinkwrap.descriptor.spi.NodeProviderImplBase (which does'n exist in shrinkwrap-descriptors project since version 1.1.0-alpha3) as soon as I set the defaultProtocol to 3.0 in arquillian.xm! Any Idea what I'm missing?

    ReplyDelete
  14. Hi Bernard,
    I have some problem with your extension to arquillian. When I run test I've got
    java.lang.NullPointerException
    at pl.itcrowd.arquillian.mock_contexts.MockContextsManager.startContexts(MockContextsManager.java:55)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:90)
    at org.jboss.arquillian.core.impl.EventContextImpl.invokeObservers(EventContextImpl.java:99)
    at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:81)
    at pl.itcrowd.arquillian.mock_contexts.MockContextsManager.startFacesContext(MockContextsManager.java:71)
    Other test which not requiring conversationScoper run very well. My test is marked as @ConversationScopeRequired. Do you have any idea what's wrong? I count on your help:) Maybe you know some workerounds?


    ReplyDelete
    Replies
    1. Have you read this?
      http://blog.itcrowd.pl/2012/06/mock-conversation.html

      Delete
  15. I don't see it before. I try this code in deployment but I don't have BeansDescriptor.class. Which library contains BeansDescriptor.class so I could add this as dependency?

    ReplyDelete
    Replies
    1. org.jboss.shrinkwrap.descriptors:shrinkwrap-descriptors-api-javaee

      Delete
    2. I configured everything as you wrote in your articles. But I still have NullPointerException when beanManager tries to get instance object of MockConversationScopedContext. BeanManager can not find bean MockConversationScopedContext.class. Do you know where is the problem.

      For details. This method returns null:
      private MockConversationScopedContext getConversationContext(){
      return getContext(MockConversationScopedContext.class);
      }

      Delete
    3. Please provide test case on GitHub.

      Delete
    4. I can't store code on public repo. I can just give you some fragments of my code. I hope it will be enough. http://wklej.to/6x5ty If you need any other fragments I will show you. Thanks for your help and sorry for bothering you.

      Delete
    5. The code you provided is not enough. You do not need post your original project. Just narrow down the problem and create test case.
      Without it it's a waste of time.

      Delete