9 Replies Latest reply on Mar 9, 2009 5:10 AM by nizzy

    Using AOP to mock out dependecies for unit-test

    nizzy

      Hi Guys,

      AOP newbie;

      I hope someone can help me I'm developing a quick AOP proof of concept with the intention to roll it out company wide. The idea is to use AOP to mock-out dependencies in order to simplify unit testing. I'm sure I don't have to state the benefits here.

      However I have created a basic J2EE project with a single SessionBean and a few entities.

      I have dependencies created in the constructor of the SessionBean which I want to mock out.

      private IPersistenceHelper helper;
      public AOPUnitTestManagerBean() {
       helper = new PersistenceHelper(PERSISTENCE_CONTEXT_NAME);
      }


      I have written an simple Interceptor which intercepts the PersistenceHelper constructor and replaces it with a MockPersistenceHelper;

      public class PersistenceHelperInterceptor implements Interceptor {
      
       /**
       * Log4j Logger for this class
       */
       private static final Logger log = Logger
       .getLogger(PersistenceHelperInterceptor.class);
      
       public String getName() {
       return "PersistenceHelperInterceptor";
       }
      
       public Object invoke(Invocation invocation) throws Throwable {
       if (log.isDebugEnabled()) {
       log.debug("inside invoke(arg0)");
       }
       Object helper = new MockPersistenceHelper(null);
       return helper;
       }
      }


      I can get this working if the MockPersistenceHelper extends the PersistenceHelper. This is not my preferred solution since it would be better if the MockPersistenceHelper implemented the same interface as the PersistenceHelper, this ensures that all methods have a mock implementations as oppsoed to simply over-riding the methods (open to abuse).

      the jboss-aop.xml to get this to work;

      <?xml version="1.0" encoding="UTF-8"?>
      <aop>
       <bind pointcut="execution(public com.xxx.unittest.aop.PersistenceHelper->new(java.lang.String))">
       <interceptor class="com.xxx.unittest.aop.interceptor.PersistenceHelperInterceptor"/>
       </bind>
      </aop>


      So I have changed it to something like;

      <?xml version="1.0" encoding="UTF-8"?>
      <aop>
      
       <bind pointcut="execution(public $typedef{persistenceHelperTypeDef}->new(java.lang.String))">
       <interceptor class="com.xxx.unittest.aop.interceptor.PersistenceHelperInterceptor"/>
       </bind>
      
       <typedef name="persistenceHelperTypeDef"
       expr="class($instanceof{com.xxx.unittest.aop.IPersistenceHelper})
       AND !class($instanceof{com.xxx.unittest.aop.mock.*})" />
      </aop>


      However every option I have tries either gives me a ClassCastException, returned object not as expected, or a Stack overflow since the MockPersistenceHelper is itself intercepted since it implements the defined interface.

      So I suppose the question is how can I define the typedef such that;

      1. It intercepts the constructor of classes of IPersistenceHelper but does not intercept those classes in the xx.xx.xx.mock package.

      Hope that wasn't too long winded to ask, hopefully, a straightforward question. This all occurs outside the JBossAS container!


        • 1. Re: Using AOP to mock out dependecies for unit-test
          stalep

          hi, the expression you want should be possible, but from just looking at the code you provided i couldnt see any immediate errors. have you tried to group the typedef together with commas? - not sure if it would help but...
          if you could provide a small example ill test it locally and probably help out more.

          • 2. Re: Using AOP to mock out dependecies for unit-test
            nizzy

            Hi Stale,

            Thanks for the response, I tried grouping the typedef with commas however I received a parser error. I had hoped this would be possible, all the documentation points toward this being possible.

            My project is rather large, is it possible to zip up the eclipse project and attach it to a post, or to send it to you? Otherwise I will create a "small" test to reproduce, and post that!

            • 3. Re: Using AOP to mock out dependecies for unit-test
              stalep

              the smaller test the better :)

              • 4. Re: Using AOP to mock out dependecies for unit-test
                nizzy

                Hi Stale,

                Couldn't get it any smaller, :)

                Cut down Session Bean

                public class AOPUnitTestManagerBean {
                
                 private IPersistenceHelper helper;
                
                 public AOPUnitTestManagerBean() {
                 helper = new PersistenceHelper(null);
                 }
                
                 public void createObject(Object o) {
                 helper.create(o);
                 }
                }


                JUnit Test
                import org.junit.Before;
                import org.junit.Test;
                
                public class AOPUnitTestManagerBeanTest {
                
                 private AOPUnitTestManagerBean man;
                
                 @Before
                 public void setUp() throws Exception {
                 man = new AOPUnitTestManagerBean();
                 }
                
                 @Test
                 public void testCreateObject() {
                
                 String s = new String("Hello");
                 man.createObject(s);
                 }
                }


                IPersistenceHelper interface
                public interface IPersistenceHelper {
                
                 public void create(Object o);
                }


                MockPersistenceHelper
                public class MockPersistenceHelper implements IPersistenceHelper {
                
                 public MockPersistenceHelper(String PERSISTENCE_CONTEXT_NAME) {
                 }
                
                 public void create(Object o) {
                 System.out.println("inside MockPersistenceHelper.create()");
                 }
                }


                PersistenceHelper
                public class PersistenceHelper implements IPersistenceHelper {
                
                 public PersistenceHelper(String persistenceContextName) {
                 }
                
                 public void create(Object o) {
                 System.out.println("inside PersistenceHelper.create()");
                 System.out.println("This should have been mocked-out --- did you set -javaagent in jvm args?");
                 }
                }


                Interceptor
                import org.jboss.aop.advice.Interceptor;
                import org.jboss.aop.joinpoint.Invocation;
                
                public class PersistenceHelperInterceptor implements Interceptor {
                
                 public String getName() {
                 return "PersistenceHelperInterceptor";
                 }
                
                 public Object invoke(Invocation invocation) throws Throwable {
                 System.out.println("Inside PersistenceHelperInterceptor.invoke");
                
                 Object helper = new MockPersistenceHelper(null);
                 return helper;
                 }
                }


                jboss-aop.xml
                <?xml version="1.0" encoding="UTF-8"?>
                <aop>
                
                 <bind pointcut="execution(public $typedef{persistenceHelperTypeDef}->new(java.lang.String))">
                 <interceptor class="PersistenceHelperInterceptor"/>
                 </bind>
                
                 <!-- This causes Stack Overflow Error since both PersistenceHelper
                 and MockPersistenceHelper implement IPersistenceHelper, causing
                 infinite loop (of sorts) -->
                 <typedef name="persistenceHelperTypeDef" expr="class($instanceof{IPersistenceHelper})" />
                
                 <!-- This causes ClassCastException -->
                 <!-- typedef name="persistenceHelperTypeDef" expr="class($instanceof{IPersistenceHelper}) AND !class(MockPersistenceHelper)" /-->
                
                </aop>


                Hope this helps

                • 5. Re: Using AOP to mock out dependecies for unit-test
                  nizzy

                  I see the same behaviour when I edit the typedef example bundled with jboss-aop-2.0.1.GA.

                  Tells me I've probably made the syntactic mistake in both :)

                  • 6. Re: Using AOP to mock out dependecies for unit-test
                    stalep

                    hi, ive tested the code you provided and i get a classcastexception too. ill take a closer look and let you know what find.

                    • 7. Re: Using AOP to mock out dependecies for unit-test
                      stalep

                      hi again. the problem is within the generated code and not in your pointcutdef. the PersistenceHelper constructor is "wrapped" in aop code when we instrument it, and that method will only accept an object of type PersistenceHelper. - not the interface. thats why it is throwing a classcastexception since it get a MockPersistenceHelper and not a PersistenceHelper.
                      atm there is no way around this, ill discuss with the team if this is something we should change, and ill let you know what we decide.

                      a solution for your problem would be to put the constructor in a method and just intercept that method. something like:

                      public class AOPUnitTestManagerBean {
                      
                       private IPersistenceHelper helper;
                      
                       public AOPUnitTestManagerBean() {
                       helper = init();
                       }
                      
                       private IPersistenceHelper init() {
                       return new PersistenceHelper(null);
                       }
                      
                       public void createObject(Object o) {
                       helper.create(o);
                       }
                      }


                      • 8. Re: Using AOP to mock out dependecies for unit-test
                        nizzy

                        Look forward to hearing from you.

                        Thanks for the help!

                        • 9. Re: Using AOP to mock out dependecies for unit-test
                          nizzy

                          Hi Stale,

                          Can I also point out that this behaviour is fundamental to being able to use AOP for unit-testing, certainly in our company, where we always have the "real" object and the "mock" object implementing the same interface.

                          I will either use you're suggested workaround for now, or make the "mock" object extend the "real" class, which also worked.

                          Thanks again for you're help.