2010-07-13

CDI + JSF 2.0. Part 2

In my previous posts I showed a sample JSF 2.0 + Maven configuration and basic features of CDI with JSF 2.0. In this article I will show you the @Produces annotation and Alternatives, as well as unit testing of CDI.


@Produces


The @Produces annotation can be applied to a (private or other) field or a method. That means that you can inject not only beans, but also collections etc. Using the @Produce annotation you may also fine the criteria when you combine it with qualifiers.

Here’s an example from the official JSR 299 document. My sample application also utilizes the annotation but in a simpler manner.

public class Shop {

@Produces @All

public List<Product> getAllProducts() { ... }

@Produces @WishList

public List<Product> getWishList() { ... }
}

You can read more about alternatives in Section 3.3.2 of JSR.


@Alternative


You may have a few possibilities for a dependency injection. You can either use qualifiers (see Part 1) or the @Alternative annotation (you have to pick either approach or your CDI implementation will complain).

If you decide to use alternatives, you choose one default bean and annotate the rest with the @Alternative annotation. Then, you can change the one to be injected using your beans.xml file.

<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
 http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
 <alternatives>
     <class>me.m1key.jsf.dao.DogsDaoMockImpl</class>
 </alternatives>
</beans>

Remember that for this to work, the two beans (the one without the @Alternative annotation and the alternative one) must be on the same level of priority, i.e. have no or the same qualifiers etc.


Testing


No modern framework or toolkit can be considered mature without making it possible to run tests in isolation, without a server. What does testing look like with CDI?

I based my research on a post by Jacek Laskowski (the text is Polish, but the source code isn’t).

You need to provide your own implementation of CDI. We are going to use Arquillian and Weld.

Here’s the relevant pom.xml part:

<dependencyManagement>
      <dependencies>
          <dependency>
              <groupId>org.jboss.weld</groupId>
              <artifactId>weld-core-bom</artifactId>
              <version>1.0.1-Final</version>
              <type>pom</type>
              <scope>import</scope>
          </dependency>
      </dependencies>
  </dependencyManagement>
  <dependencies>
      <dependency>
          <groupId>javax.inject</groupId>
          <artifactId>javax.inject</artifactId>
          <version>1</version>
          <type>jar</type>
          <scope>provided</scope>
      </dependency>
      <dependency>
          <groupId>javax.enterprise</groupId>
          <artifactId>cdi-api</artifactId>
          <version>1.0-SP1</version>
          <type>jar</type>
          <scope>provided</scope>
      </dependency>
      <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.8.1</version>
          <type>jar</type>
          <scope>test</scope>
      </dependency>
      <dependency>
          <groupId>org.jboss.arquillian</groupId>
          <artifactId>arquillian-junit</artifactId>
          <version>1.0.0.Alpha2</version>
          <scope>test</scope>
      </dependency>
      <dependency>
          <groupId>org.jboss.arquillian.container</groupId>
          <artifactId>arquillian-weld-embedded</artifactId>
          <version>1.0.0.Alpha2</version>
          <scope>test</scope>
      </dependency>
      <dependency>
          <groupId>org.jboss.weld</groupId>
          <artifactId>weld-core</artifactId>
          <version>1.0.1-Final</version>
          <scope>test</scope>
      </dependency>
      <dependency>
          <groupId>org.jboss.weld</groupId>
          <artifactId>weld-api</artifactId>
          <version>1.0</version>
          <scope>test</scope>
      </dependency>
      <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-simple</artifactId>
          <version>1.5.10</version>
          <scope>test</scope>
      </dependency>
      <dependency>
          <groupId>javax.el</groupId>
          <artifactId>el-api</artifactId>
          <version>2.2</version>
          <scope>test</scope>
      </dependency>
      <dependency>
          <groupId>org.jboss.weld.servlet</groupId>
          <artifactId>weld-servlet</artifactId>
          <version>1.0.1-Final</version>
          <scope>test</scope>
      </dependency>
  </dependencies>
  <repositories>
      <repository>
          <id>JBoss.org</id>
          <name>JBoss Repository</name>
          <url>http://repository.jboss.com/maven2</url>
      </repository>
      <repository>
          <id>jboss-public-repository-group</id>
          <name>JBoss Public Maven Repository Group</name>
          <url>https://repository.jboss.org/nexus/content/groups/public/</url>
      </repository>
  </repositories>

And now the test itself.

package me.m1key.jsf;

import static org.junit.Assert.assertNotNull;

import java.io.File;
import java.io.IOException;

import javax.inject.Inject;

// ..

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.spec.JavaArchive;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(Arquillian.class)
public class DogsDaoTest {

  @Deployment
  public static JavaArchive createTestArchive()
          throws IllegalArgumentException, IOException {
      return ShrinkWrap.create("test.jar", JavaArchive.class)
              .addManifestResource(new File("src/test/resources/beans.xml"),
                      ArchivePaths.create("beans.xml")).addClasses(
                      DogsDaoImpl.class, DogsDaoMockImpl.class);
  }

  @Inject
  private DogsDao dogsDao;

  @Test
  public void testInjectedDaoNotNull() {
      assertNotNull(dogsDao);
      System.out.println(dogsDao.getClass().getCanonicalName());
  }

}

It’s a Maven project - and our test resources folder will contain another beans.xml. In this beans.xml we will specify alternative dependencies to inject (mock DAOs).

<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
 http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
 <alternatives>
     <class>me.m1key.jsf.dao.DogsDaoMockImpl</class>
 </alternatives>
</beans>

  • @RunWith(Arquillian.class) - we need this for Arquillian to be activated for the tests
  • @Deployment - it is an Arquillian’s requirement for one method with this annotation to be present. It builds an archive which contains all our tests need to run. You must manually specify which classes it should use. We are also loading the other beans.xml with alternative beans specified
  • @Inject - here a bean will be injected by our combination of Arquillian and Weld (namely, DogsDaoMockImpl)
  • The test method only checks if anything was injected


Summary


I showed you how to use the @Produce and @Alternative annotations. You can check out the source code to see it working. NOTE: I removed qualifiers so that @Alternative can work.

We also looked at testing possibilities from JUnit .

Download sample source code for this article

1 comment: