Using the DeltaSpike CDI container tests

Let us take a different tack. Although there is no standard API for using CDI outside of a Java EE environment such as standalone Java SE, there is a project called DeltaSpike. This is an open source project that defines a wrapper API around two common CDI implementations JBoss Weld and Apache Open Web Beans. The URL is http://deltaspike.apache.org/.

Let us modify the Gradle build script to add dependencies for the Weld container as follows:

// Define equivalent Maven GAV coordinates.
group = 'com.javaeehandbook.book1'
archivesBaseName = 'ch02-cdi-standalone'
version = '1.0'
ext.deltaspikeVersion = '0.3-incubating'

dependencies {
  compile 'org.jboss.weld.se:weld-se-core:1.1.9.Final'
  compile 'org.slf4j:slf4j-simple:1.6.1'
  compile "org.apache.deltaspike.cdictrl:deltaspike-cdictrl-api: ${deltaspikeVersion}"
  compile "org.apache.deltaspike.cdictrl:deltaspike-cdictrl-weld: ${deltaspikeVersion}"
  
  testCompile 'junit:junit:4.11'
  }

This is almost the same definition as before, except for the dynamic property deltaspikeVersion, which allows the version number of the dependency to be configured to be changed. Incidentally, you can switch to the other DeltaSpike implementation, Apache Open Web Beans, by changing the dependency to:

dependencies {
  compile 'org.apache.openwebbeans:openwebbeans-impl:1.1.6'	
  compile 'org.apache.openwebbeans:openwebbeans-ee:1.1.6'
  compile "org.apache.deltaspike.cdictrl: deltaspike-cdictrl-api: ${deltaspikeVersion}"
  compile "org.apache.deltaspike.cdictrl: deltaspike-cdictrl-owb: ${deltaspikeVersion}"
  /* ... */
  }

Let us create a couple of qualifier annotations. We create one to represent a premium and expensive service, and a second to represent a budget service.

// Economy.java
@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Economy { }

// Premium.java
@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Premium { }

The preceding annotation must exist in separate Java class files. Next, we will create a traveler service for airlines. The service is not at all useful, but it will demonstrate the testing, the qualifiers, and the dependency injection. It will simply allow us to retrieve a random flight given an airline code.

public interface TravelService {
  FlightSet findRandomByAirline(String airline);
}

The simplified definition of the budget travel service, unsurprisingly, called BudgetTravelServiceImpl is as follows:

@Economy
public class BudgetTravelServiceImpl implements TravelService {
  @Override
  public FlightSet findRandomByAirline(String airline) {
    Airline airlineBudget = new Airline("CHP","Cheap Budget");
    return new FlightSet(Arrays.asList(new AirlineRoute("LGW", "DUB", parseDate("20131110-12:30:00 GMT"), parseDate("20131110-14:00:00 GMT"), airlineBudget, 69.0), new AirlineRoute("LHW", "PAR", parseDate("20131110-16:45:00 -0500 GMT"), parseDate("20131110-20:00:00 -0700 +0100"), airlineBudget, 79.0)));
  }
}

We can also define an expensive service called TravelFunkServiceImpl as follows:

@Premium
public class TravelFunkServiceImpl implements TravelService {
  @Override
  public FlightSet findRandomByAirline(String airline) {
    Airline airlineBrit = new Airline("BRS","British Stars");
    return new FlightSet(Arrays.asList(new AirlineRoute("NYC", "SFO", parseDate("20131110-16:45:00 -0500"), parseDate("20131110-20:00:00 -0700"), airlineBrit, 250.0)));
  }
}

So now, we write an elegant and a simple unit test with the DeltaSpike library to verify the CDI container (either JBoss Weld or Apache Open Web Beans) injects the correct travel service according to the qualifier annotation.

package je7hb.travelfunk;
import je7hb.standalone.*;
import org.junit.Test;
import javax.inject.Inject;
import static org.junit.Assert.assertNotNull;

public class TravelServiceTest extends AbstractCdiContainerTest {
  @Inject @Premium TravelService premiumTravelService;
  @Inject @Economy TravelService economyTravelService;
  
  @Test
  public void shouldInjectPremiumService() {
    System.out.printf("premiumTravelService=%s
", premiumTravelService);
    assertNotNull(premiumTravelService);
    FlightSet flight = premiumTravelService.findRandomByAirline("BRS");
    assertNotNull( flight );
  }
  
  @Test
  public void shouldInjectEconomyService() {
    System.out.printf("economy=%s
", economyTravelService);
    assertNotNull(economyTravelService);
    FlightSet flight = economyTravelService.findRandomByAirline("BRS");
    assertNotNull(flight);
  }
}

The distilled magic of the DeltaSpike library lies within the AbstractCdiContainerTest implementation, which is part of this book's source code project. For JUnit testing, we need to make sure that we have a brand new CDI container for the invocation of the test methods. There is a restriction for the CDI container, in that they do not play very well in parallel executions from the same Java ClassLoader; therefore, before each test method is invoked, we first have to make sure that the container is initialized appropriately and also the conversation context is cleared. The following source code is targeted for JUnit; you will need to modify it accordingly for another testing framework such as TestNG.

First, the CDI container is created with the static method of CdiContainerLoader, which will retrieve a JBoss Weld or Apache Open Web Beans. The container is instantiated in the static helper method startUpContainer(), because we want only to initialize the test container when the test class is loaded, and before test methods are invoked on it. Once we have the CdiContainer type, we boot up, and then start all of the conversational contexts in it.

import org.apache.deltaspike.cdise.api.*;
import org.junit.*;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.*;

public abstract class AbstractCdiContainerTest {
  protected static CdiContainer cdiContainer;
  
  @Before
  public final void setUp() throws Exception {
    cdiContainer.getContextControl()
    .stopContext(RequestScoped.class);
    cdiContainer.getContextControl()
    .startContext(RequestScoped.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);
  }
  @After
  public final void tearDown() throws Exception {
    if (cdiContainer ! = null) {
      cdiContainer.getContextControl()
      .stopContext(RequestScoped.class);
      cdiContainer.getContextControl()
      .startContext(RequestScoped.class);
    }
  }
  
  @BeforeClass
  public final synchronized static void startUpContainer()
  throws Exception {
    cdiContainer = CdiContainerLoader.getCdiContainer();
    cdiContainer.boot();
    cdiContainer.getContextControl().startContexts();
    }
  
  @AfterClass
  public final synchronized static void shutdownContainer()
  throws Exception {
    if (cdiContainer ! = null) {
      cdiContainer.shutdown();
      cdiContainer = null;
    }
  }
}

After JUnit invokes the test methods of the class, we ask the CDI container to shut down. This procedure is illustrated in the method shutdownContainer().

The other two methods, setUp() and tearDown(), are designed to start and stop conversational context before and after a test method is invoked. In the setUp() method, we make sure that the qualifier annotations are injected into the test class instance before the test method is invoked. In the previous unit test, the CDI container will look up the travel service TravelService and find the correct type of service according to qualifier, for example, @Premium or @Economy.

Typically, to simulate the request arriving from a web service client, the request scope context should be restarted for each test. Therefore, in the tearDown() method, we explicitly stop and restart the request conversational scope context.

To complete the example, here is the utility class, Util, which contains a static method to parse the formatted date, time, and time zone.

import java.text.SimpleDateFormat;
import java.util.Date;

public final class Utils {
  private final static SimpleDateFormat FORMATTER = new SimpleDateFormat("yyyyMMdd-hh:mm:ss Z");
  public final static Date parseDate(String s) {
    try {
      Date date = FORMATTER.parse(s);
      return date;
    }
    catch (Exception e) {
      throw new RuntimeException("unable parse date time from string ["+s+"]",e);
    }
  }
  
  private Utils() { }
}

Now that we understand the CDI qualifiers, let's move forward.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.235.23