| FundamentalsTest.java |
package org.mockejb.test;
import java.rmi.RemoteException;
import java.security.Principal;
import javax.rmi.PortableRemoteObject;
import javax.naming.*;
import javax.ejb.*;
import org.easymock.MockControl;
import org.mockejb.*;
import org.mockejb.interceptor.*;
import org.mockejb.jndi.*;
/**
* Main MockEjb test case. Demonstrates the basic scenarios of MockEjb usage.
* This test runs in two modes. When Cactus mode is turned on (using
* "mockejb.cactus.mode" system property), it will run as the Cactus test (on the server).
* In this case, the test class will use EJBs deployed by the app server.
* Otherwise it will run outside of the app server under MockContainer.
* Cactus mode is supported by OptionalCactusTestCase superclass.
*
* @author Alexander Ananiev
*/
public class FundamentalsTest extends OptionalCactusTestCase implements Interceptor {
// State of this test case. These variables are initialized by setUp method
private SampleService sampleService;
private SampleServiceHome sampleServiceHome;
private Context context;
// Aspect system used by this test
private AspectSystem aspectSystem;
private MockContainer mockContainer;
/**
* Flag which is set by our custom interceptor if the method that
* it checks was called. See testCustomInterceptor.
*/
private boolean wasInvoked = false;
public FundamentalsTest(String name) {
super(name);
}
/**
* Deploys and creates EJBs needed for our tests.
* Note that JUnit runs this method for every test method, but since
* MockEjb deployment is purely an in-memory operation,
* it does not have any performance penalty.
* During deployment, MockEjb simply re-binds EJBs to the same keys in the JNDI tree.
*/
public void setUp() throws Exception {
/*
* since some of the tests dynamically add interceptors,
* we need to initialize our AspectSystem
* You may want to use "interceptor.aspect.system.thread" system
* property in which case AspectSystem is create per thread so the
* tests can run concurrently.
*/
aspectSystem = AspectSystemFactory.getAspectSystem();
/* When the test runs on the server (in cactus mode) we want to be able
* to look up EJBs and resources already deployed in the container.
* This allows us to run the mix of "true" and mock EJBs in the same test.
*/
if ( isRunningOnServer() ) {
/* MockContext will look up objects in the tree of the
* app server if its environment is set as the delegate.
* Since we're already inside the app server, we assume that we can use
* its initial context to delegate to.
* Note that we must do it before we set MockContextFactory as initial.
*/
MockContextFactory.setDelegateContext( new InitialContext() );
}
/* We need to set MockContextFactory as our provider.
* This method sets the necessary system properties.
* We can't simply pass hashtable to the InitialContext
* because our tested EJBs have "new InitialContext()" in their code.
* So we assume that all participants of this test don't use
* the constructor of the InitialContext that takes Hashtable.
*/
MockContextFactory.setAsInitial();
// create the initial context that will be used for binding EJBs
context = new InitialContext( );
/* Create an instance of the MockContainer.
* MockContainer also cleans all currently added aspects/interceptors and
* adds system interceptors that it needs, such as ExceptionHandler
* to make sure that we always start from a known state.
*/
mockContainer = new MockContainer( context );
// if the test runs outside of the container (app server)
if (! isRunningOnServer()) {
/* Deploy EJBs to the MockContainer if we run outside of the app server
* In cactus mode all but one EJB are deployed by the app server, so we don't need to
* do it.
*/
/* Create deployment descriptor of our sample bean.
* MockEjb does not support XML descriptors.
*/
SessionBeanDescriptor sampleServiceDescriptor =
new SessionBeanDescriptor( SampleService.JNDI_NAME,
SampleServiceHome.class, SampleService.class, new SampleServiceBean() );
// Deploy operation simply creates Home and binds it to JNDI
mockContainer.deploy( sampleServiceDescriptor );
// StatelessSampleBean calls SampleHelperBean, so we need to deploy it too
/* MockEjb does not currently support true bean-scoped JNDI context, so
* every JNDI name must be unique within the test.
* Therefore we use JNDI name required by SampleBean (configured via ejb-ref in the real EJB)
* to access HelperBean
*/
SessionBeanDescriptor helperBeanDescriptor =
new SessionBeanDescriptor( SampleServiceBean.HELPER_BEAN_JNDI_NAME,
SampleHelperHome.class, SampleHelper.class, new SampleHelperBean() );
mockContainer.deploy( helperBeanDescriptor );
}
/*
* Deploy mock implementation of the ExternalService session bean.
* ExternalService is not part of the unit we're testing (SampleBean),
* so we don't want to rely on its implementation (which might depend on
* other session beans or other resources).
* We use EasyMock framework to create an implementation that
* simply returns mock test data.
* If you run this class inside the container and real ExternalService session
* bean is deployed, our mock
* implementation will override it (just for the duration of the test).
*/
// use easymock to program mock bean. Note that you don't need to worry about callback
// methods, MockEJB "understands" that the class is a mock class and won't try to
// call callback methods on it.
MockControl externalServiceControl = MockControl.createControl(ExternalService.class);
ExternalService externalServiceBean = (ExternalService) externalServiceControl.getMock();
externalServiceBean.sampleMethod();
externalServiceControl.setReturnValue("sample string");
externalServiceControl.replay();
mockContainer.deploy( new SessionBeanDescriptor( ExternalService.JNDI_NAME,
ExternalServiceHome.class, ExternalService.class, externalServiceBean ) );
// All EJBs are now deployed
// To get the Sample bean we use the standard J2EE routine
// Lookup the home
Object sampleHomeObj = context.lookup( SampleService.JNDI_NAME );
// PortableRemoteObject does not do anything in our case but we can still call it
sampleServiceHome = (SampleServiceHome) PortableRemoteObject.narrow(sampleHomeObj,
SampleServiceHome.class );
// create the bean
sampleService = sampleServiceHome.create();
}
/**
* Performs the necessary cleanup by restoring the system properties that
* were modified by MockContextFactory.setAsInitial().
* This is needed in case if the test runs inside the container, so it would
* not affect the tests that run after it.
*/
public void tearDown() {
// Inside the container this method does not do anything
MockContextFactory.revertSetAsInitial();
}
/**
* Tests standard Home and EJBMetaData methods.
*/
public void testHome() throws Exception {
// Test the support of EJB metadata
EJBMetaData ejbMetaData = sampleServiceHome.getEJBMetaData();
assertEquals( ejbMetaData.getHomeInterfaceClass(),
SampleServiceHome.class );
// note how you can use simple "equals" with EJBs and their homes
assertEquals( sampleServiceHome, ejbMetaData.getEJBHome() );
// toString also supported
System.out.println(sampleServiceHome);
// Test standard EJB methods
assertEquals( sampleService.getEJBHome(), sampleServiceHome);
assertTrue( sampleService.isIdentical( sampleService ));
/* make sure that we can serialize home and the bean
* in case if we run outside of the app server
*/
if ( !isRunningOnServer() ) {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(baos);
oos.writeObject(sampleServiceHome);
oos.writeObject(sampleService);
oos.close();
}
}
/**
* Tests simple EJB invocations
*/
public void testSimpleCalls() throws Exception {
// Call a simple business method
String s = sampleService.echoString( "test");
assertEquals( s, "test" );
// now call the method that invokes another bean
assertNotNull( sampleService.invokeOtherBean() );
// now make a call to the external service
sampleService.invokeExternalService();
/* We can even examine the internal state of the bean if there is a need.
* Any EJB created by MockEJB can be casted to the special interface that
* provides access to the bean and its context
*/
SampleServiceBean sampleServiceBean =(SampleServiceBean) ((EjbBeanAccess)sampleService).getBean();
// we have no state to check for this test. Just making sure that it works
assertNotNull(sampleServiceBean);
}
/**
* Test MockEJB exception handling.
* According to the EJB spec, system exception must be wrapped in
* RemoteException.
* The exception from the local interface must be wrapped in EJBException.
* MockEjb treats all runtime and transaction-related exceptions as system exceptions.
*/
public void testExceptionHandling() throws Exception {
try{
sampleService.throwSystemException();
}
catch ( RemoteException remoteEx ){
assertTrue( remoteEx.detail instanceof EJBException );
assertTrue( ((EJBException)remoteEx.detail).getCausedByException()
instanceof RuntimeException );
}
// Example of the application exception.
// Container should pass this exception through without changes.
try{
sampleService.throwAppException();
fail("We did not get the application exception ");
}
catch( Exception ex ){
}
}
/**
* Demonstrates the use of custom interceptors.
* This test class implements Interceptor interface. It will verify
* that not-null parameter is passed to HelperBean.
* The same thing can be done using InvocationRecorder
* (see testInvocationRecorder).
* We can only run this test if it is being executed outside of the
* container.
*/
public void testCustomInterceptor() throws Exception {
// This won't work in the container since it does not support interceptors
if ( isRunningOnServer( ))
return;
// Set our custom interceptor so it would handle all calls to SampleHelper interface (w/o subclasses)
aspectSystem.add( new ClassPointcut( SampleHelper.class, false), this);
// flag indicating that the EJB under test was called
wasInvoked = false;
// we can now invoke our test method
// In case of problems, "asserts" in the invoker will fail the test.
// This method calls HelperBean method
sampleService.invokeOtherBean();
// we need to make sure that the method was invoked
assertTrue( wasInvoked );
}
/**
* InvocationRecorder interceptor provides an easy way to inspect the
* data about the calls.
* If it added to the interceptor list, it will record the information about
* all calls to the bean.
* We'll use it to check that SampleBean called HelperBean and passed non-null
* parameter.
* We can only run this test if it is being executed outside of the
* container.
*/
public void testInvocationRecorder() throws Exception {
// This won't work in the container since it does not support interceptors
if ( isRunningOnServer( ))
return;
InvocationRecorder recorder = new InvocationRecorder();
// Set the recorder so it would record all calls to SampleHelper interface (w/o subclasses)
aspectSystem.add( new ClassPointcut( SampleHelper.class, false), recorder);
/*
* We can also capture the calls to the create method of the SampleHelperHome
* We don't really need to do it here, it just demonstrates that you can intercept
* "create" methods.
*/
aspectSystem.add( new MethodPatternPointcut( "SampleHelperHome\\.create()" ), recorder);
/*
* Or how about intercepting JNDI lookups?
*/
aspectSystem.add( new MethodPatternPointcut( "Context\\.lookup" ), recorder);
// recorder is now active, it will preserve the info about all calls to SampleHelper
// we can now invoke our test method
// This method calls HelperBean method
sampleService.invokeOtherBean();
// Make sure that the method of our interest was invoked
// Tests that the JNDI lookup was called
assertNotNull( recorder.findByProxyMethod( "Context\\.lookup") );
// Test that the lifecycle methods were invoked by the container (triggered by "create")
assertNotNull( recorder.findByProxyMethod( "SampleHelperHome\\.create()") );
// Test that the dummy method was invoked
InvocationContext dummyMethodInvocation = recorder.findByTargetMethod( "dummyMethod");
assertNotNull(dummyMethodInvocation);
// make sure that the parameter was not null
assertNotNull( dummyMethodInvocation.getParamVals()[0]);
// since the aspect system is re-initialized in setUp we don't need to
// clean the recorder we just added
}
/**
* This method demonstrates how to test beans that makes the use of
* use of the security-related methods of EJBContext (getCallerPrincipal, isCallerInRole).
*/
public void testSecurity() throws Exception {
/* Create the mock user and "login". Login method
* simply puts the user on the thread so EJBContext implementation
* can get to it.
* This will create user without any roles.
*/
mockContainer.login( new MockUser( "testuser"));
Principal principal = sampleService.getPrincipal();
assertEquals("testuser", principal.getName());
// Now let's add roles
mockContainer.login( new MockUser( "testuser", new String[] {"role1","role2"}));
// "hasRole" simply returns the result of "isCallerInRole"
assertTrue( sampleService.hasRole("role1") );
assertTrue( sampleService.hasRole("role2") );
assertFalse( sampleService.hasRole("fake_role") );
// If you don't login, MockContainer logs in as anonymous user,
// which you can do as well:
mockContainer.login( MockUser.ANONYMOUS_USER );
}
/**
* Implementation of the Interceptor interface that can be used
* for testing various preconditions/postconditions of the EJB call.
* Here we use it for very simple check to make sure that non-null parameter
* is passed to the HelperBean.
*/
public void intercept( InvocationContext invocationContext ) throws Exception {
// Make sure that we run the test for the right method
if ( SampleHelperBean.class.isInstance( invocationContext.getTargetObject() ) &&
invocationContext.getTargetMethod().getName().equals("dummyMethod")) {
System.out.println("Running test invoker for "+invocationContext.getTargetMethod() );
// Test that SampleBean is passing parameter to the HelperBean
assertNotNull( invocationContext.getParamVals()[0] );
wasInvoked = true;
}
// We're done, move to the next interceptor from the list
invocationContext.proceed( );
}
/**
* Example of a mock session bean implementing ExternalService interface.
* Note how MockEJB allows to specify a bean implementation without having to
* provide mandatory callback methods.
* This class is not used. It is here only for demo purposes.
* We use Easymock to create mock implementation
* classes.
*/
public static class ExternalServiceMockBean {
public String sampleMethod(){
return "sample result";
}
}
}