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"; } } }