Ilias Tsagklis

About Ilias Tsagklis

Ilias Tsagklis is a senior software engineer working in the telecom domain. He is an applications developer in a wide variety of applications/services. Ilias is co-founder and Executive Editor at Java Code Geeks.

Integration testing scoped beans in CDI 1.0 and Spring 3.1

In this blog post I describe how to do integration testing with scoped beans in Spring and CDI. Everything is illustrated with small code samples. Integration testing with scopes is not particular easy. Imagine a bean that lives in the session scope, like UserCredentials. In an integration test you typically have no HttpRequest or HttpSession to work on (at least if you are not doing tests that include your user interface). Therefore you need some infrastructure for integration testing. With both technologies it is a little puzzling to get this infrastructure going. Get your own picture of it.

If you are new to scope and context in CDI and Spring check out the basics and get an overview about the different scopes.

Integration testing scoped beans in Spring

In Spring 3.1 there is no integration test support for scoped session or request beans (see here). It is scheduled for Spring Version 3.2. However, this link explains a solution that worked for me.

First you need to develop a SessionScope for the test. It’s purpose is to Mock a HttpRequest and a HttpSession.

package com.mycompany.springapp.scope;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.SessionScope;

public class SetupSession extends SessionScope implements InitializingBean {

 public void afterPropertiesSet() throws Exception {
  MockHttpServletRequest request = new MockHttpServletRequest();
  MockHttpSession session = new MockHttpSession();
  request.setSession(session);
  RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(
    request));
 }

}

To register that class as the session scope management object in your test-beans.xml do this:

 

 
  
   
    
     
    
    
     
    
   
  
 

Notice that I registered the scopes after the context:component-scan tag.

Finally, I wrote my test class:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.Assert;

@ContextConfiguration("/test-beans.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class MyScopeBeanTest {
 
 @Autowired
 private MyScopeBean myScopeBean;
 
 @Test
 public void testBeanScopes() {
  Assert.isTrue(myScopeBean.getMyCustomScopedService().getName().equals("Test"));
  Assert.isTrue(myScopeBean.getMySessionScopedService().getName().equals("Test"));
 }

}

Notice that I have called a method getName() on the scoped bean. This is necessary to ensure that scoping works. The client proxy may get injected at the injection point, but if you make a call to the proxy it does not have a reference to a scope object and a collaborating object respectively.

Integration testing scoped beans with CDI

The tool I used for integration testing CDI is Arquillian. There are alternatives. You could use Weld “natively” if you only test with CDI classes. But if you also have EJB, that’s not sufficient. Arquillian comes with a reasonable amount of transitive dependencies. Let’s see how to get the stuff going.

Note: without Maven you’re lost in the desert here, so I encourage you to use it! I have tried m2eclipse for Helios, it did not work for me, I went back to good old command line using Maven 3.

Changes to your pom.xml file

These samples assume you have a Java EE project working, you can also see here how to set-up a new Java EE 6 project. To integrate Arquillian make the following changes to your pom.xml file:

In the properties section:

      1.0.0.Alpha5

Add this repository:

  repository.jboss.org

http://repository.jboss.org/nexus/content/groups/public

  default
  
     true
     never
     warn
  
  
     false
     always
     warn
  

This is the official JBoss Maven repository where all the Arquillian distributions are available.

Add the following dependencies to your pom.xml:

 
   junit 
   junit 
   4.8.1 
   test 
 


   org.jboss.arquillian
   arquillian-junit
   ${arquillian.version}
   test



   org.jboss.arquillian.container
   arquillian-glassfish-remote-3.1
   ${arquillian.version}
   test



   javax.enterprise
   cdi-api
   1.0-SP4
   test

The first dependency is your JUnit framework to write integration tests. The second dependency integrates Arquillian with JUnit. The third dependency integrates your deployment container. For me that is my Glassfish installation. The last dependency is the CDI API that needs to be available for your CDI tests.

Notice in the line 17, I am using my Glassfish 3.1 installation as deployment container and Arquillian uses remote calls to perform the tests. You need to configure your own deployment environment here. See the JBoss Maven Repo for the correct artifactId value. With Arquillian your target environment can also be an embedded container such as JBoss Embedded AS, GlassFish Embedded or Weld SE. In that case, you don’t need a seperate container installation and remote calls, all is running locally (“in-memory”).

You do a mvn eclipse:eclipse after you added the dependencies for your target environment.

Writing and executing a test with Arquillian and JUnit

Finally I wrote my first Arquillian integration test class:

import javax.inject.Inject;

import junit.framework.Assert;

import org.jboss.arquillian.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ArchivePaths;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Test;
import org.junit.runner.RunWith;

import com.mycompany.jeeapp.scope.MyApplicationService;
import com.mycompany.jeeapp.scope.MyConversationService;
import com.mycompany.jeeapp.scope.MyDefaultService;
import com.mycompany.jeeapp.scope.MyRequestService;
import com.mycompany.jeeapp.scope.MyScopeBean;
import com.mycompany.jeeapp.scope.MySessionService;
import com.mycompany.jeeapp.scope.MySingletonService;
import com.mycompany.jeeapp.scope.extension.MyCustomScopeService;

@RunWith(Arquillian.class)
public class MyArquillianJUnitTest {

 @Inject
 private MyScopeBean myScopeBean;

 @Deployment
 public static JavaArchive createTestArchive() {
  return ShrinkWrap
    .create(JavaArchive.class, "test.jar")
    .addClasses(MyScopeBean.class,MyApplicationService.class,
      MyConversationService.class, MyDefaultService.class,
      MyRequestService.class, MySessionService.class,
      MySingletonService.class, MyCustomScopeService.class)
    .addAsManifestResource(EmptyAsset.INSTANCE,
      ArchivePaths.create("beans.xml"));
 }

 @Test
 public void testScopedBeans() {
  Assert.assertTrue(myScopeBean.getApplicationService().getSomeName()
    .equals("myName"));
  Assert.assertTrue(myScopeBean.getApplicationServiceWithNew().getSomeName()
    .equals("myName"));
  Assert.assertTrue(myScopeBean.getCustomScopeService().getSomeName()
    .equals("myName"));
  Assert.assertTrue(myScopeBean.getDefaultService().getSomeName()
    .equals("myName"));
  Assert.assertTrue(myScopeBean.getRequestService().getSomeName()
    .equals("myName"));
  Assert.assertTrue(myScopeBean.getSessionService().getSomeName()
    .equals("myName"));
  Assert.assertTrue(myScopeBean.getSingletonService().getSomeName()
    .equals("myName"));
 }

}

Conclusion

Spring does not offer an integrated test support for scoped beans at the moment. This was very surpising as Spring always attached major importance to all test topics. There is a workaround that I have described in my blog. It wasn’t difficult to make that work. Full integration test support is scheduled for Release 3.2 M1.

CDI scoped beans testing is enabled with Arquillian. I had some problems during set-up (see last paragraph below) which I think is usual if you use a new technology. The fact that you have to pass all the beans under test to the archive (see @Deployment method) is something I need to try in a large project: is that really a good idea? Sometimes, large applications are wired together with dozens of beans from different packages. It’s difficult to predict which beans are used in an integration test.

Problems and solutions

Some Arquillian set-ups come with so many dependencies that you cannot use standard Eclipse launch configurations. The command line argument that is generated exceeds the Windows length limit for command line instructions. Therefore I have used an Ant-Script to start my test. The script is just for illustration. You have to build your own own Ant script. You can get your classpath information as follows: in Eclipse, go to “File > Export > General > Ant buildfiles” to generate your classpath information. Take this classpath info and drop it into your Ant JUnit test start script. I have documented my complete Ant script here.

When I started this Ant script then everything worked fine for me. If you have any issues let me know, you can look into your test results file and into the server.log to analyse.

More error messages during Arquillian set-up

WELD-001303 No active contexts for scope type javax.enterprise.context.ConversationScoped

-> ConversationScope is bound to JSF by EE spec. So they won’t be active during a normal HTTP request which is what Arquillian is piggybacking on.

POST http://localhost:4848/management/domain/applications/application returned a response status of 403

-> 404/403 errors could be deployment issues, check server.log for the root cause (mine was that I did not have all the required classes added to the test.jar)

Exception occurred executing command line.

Cannot run program “D:\dev_home\java-6-26\bin\javaw.exe” (in directory “D:\dev_home\repositories\git\jee-app-weld\jee-app-weld”): CreateProcess error=87, Falscher Parameter

-> Classpath exceeds allowed length for windows command line operations. You need to use an Ant script or Maven to run the tests.

ValidationException: DeploymentScenario contains targets not maching any defined Container in the registry

-> see here.

WELD-000072 Managed bean declaring a passivating scope must be passivation capable. Bean: Managed Bean [class com.mycompany.jeeapp.scope.example.UserCredentials] with qualifiers [@Any @Default]

-> You need to implement Serializable on Session- and Conversation-Scoped beans.

DeploymentScenario contains targets not maching any defined Container in the registry. _DEFAULT_

-> see here.

java.net.ConnectException: Connection refused: connect

-> Your remote Java EE server installation is not running, start it!

Reference: “Integration testing scoped beans in CDI 1.0 and Spring 3.1″ from our JCG partner Niklas.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

Leave a Reply


eight − = 6



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close