Testing Seam 2 based applications with Needle
Posted by heyw in JBoss Blog on Mar 8, 2012 6:11:32 AMWriting good concise tests for application components can be quite cumbersome and difficult. Especially, when they have a lot of dependencies to other beans and you do not want to manually write mock objects to fulfil all of them. On the other hand, you do not want to run too many slow integration tests.
With Needle you can accomplish those goals in very comfortable way.
Needle is a lightweight framework for testing Java EE components in isolation outside of the container. Objects under test will get their dependencies injected automatically. You are free to supply implementations yourself or rely on mock objects provided by Needle.
Needle is fully extensible, you may implement your own injection providers or register additional annotations, e.g for Seam 2 injection.
Example Seam component:
@Name(AuthenticatorService.NAME) @Stateless public class AuthenticatorServiceBean implements AuthenticatorService { @Logger private Log log; @In private Credentials credentials; @In private Events events: @EJB private UserDao userDao; @Out(required = false, scope = ScopeType.SESSION) private User currentUser; @Override public boolean authenticate() { log.info("authenticate user #0", credentials.getUsername()); User user = userDao.findByUsername(credentials.getUsername()); if (user != null && user.verifyPassword(credentials.getPassword())) { currentUser = user; events.raiseEvent("authenticate", user); return true; } return false; } }
The following example demonstrates hwo to write a simple Needle test. Our test based on JUnit 4 and the EasyMock framework. EasyMock is a framework to create Mock Objects to test components in isolation. Needle provides JUnit Rules to extend JUnit. Rules are basically wrappers around test methods. They may execute code before, after or instead of a test method.
public class AuthenticatorServiceBeanTest { @Rule public NeedleRule needleRule = new NeedleRule(); @ObjectUnderTest private AuthenticatorServiceBean authenticator; @EJB private UserDao userDaoMock; private EasyMockProvider mockProvider = needleRule.getMockProvider(); @InjectIntoMany private Credentials credentials = new Credentials(); @Test public void testLoginFailed() throws Exception { EasyMock.expect( userDaoMock.findByUsername(EasyMock.<String> anyObject())) .andReturn(null); mockProvider.replayAll(); boolean login = authenticator.authenticate(); Assert.assertFalse(login); mockProvider.verifyAll(); } @Test public void testLoginWithWrongPassword() throws Exception { String username = "username"; credentials.setUsername(username); credentials.setPassword("any"); mockProvider.resetToStrict(userDaoMock); EasyMock.expect(userDaoMock.findByUsername(username)).andReturn( new UserTestdataBuilder().withUsername(username).build()); mockProvider.replayAll(); boolean login = authenticator.authenticate(); Assert.assertFalse(login); mockProvider.verifyAll(); } @Test public void testLoginSuccess() throws Exception { String username = "username"; credentials.setUsername(username); credentials.setPassword("secret"); mockProvider.resetToStrict(userDaoMock); EasyMock.expect(userDaoMock.findByUsername(username)).andReturn( new UserTestdataBuilder().withUsername(username).build()); mockProvider.replayAll(); boolean login = authenticator.authenticate(); Assert.assertTrue(login); mockProvider.verifyAll(); } }
The Needle rule does the real magic: it scans the test for all fields annotated with @ObjectUnderTest and initializes these tested components by injection of their dependencies. By default, Mock objects for the dependencies are created and injected.
The following configuration shows the registration of Seam 2 annotations in the needle.properties. Needle will look for the configuration somewhere in the classpath.
custom.injection.annotations=org.jboss.seam.annotations.In,org.jboss.seam.annotations.Logger
If you are using Maven as your build tool add the following dependencies to your pom.xml file to get started with Needle:
<dependency> <groupId>de.akquinet.jbosscc</groupId> <artifactId>jbosscc-needle</artifactId> <scope>test </scope> <version>2.1</version> </dependency>
We need the following dependecies to use Needle with JUnit and EasyMock. You are free to use other Test- and Mock-Frameworks, such TestNG with Mockito.
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> <dependency> <groupId>org.easymock</groupId> <artifactId>easymock</artifactId> <version>3.1</version> <scope>test</scope> </dependency>
The complete example is available with our Seam Archetype.
mvn archetype:generate \ -DarchetypeArtifactId=jbosscc-seam-archetype \ -DarchetypeGroupId=de.akquinet.jbosscc \ -DarchetypeVersion=1.3
More examples and documentation about Needle are available on the Needle project page: http://needle.spree.de.
Comments