JMock and AspectJ

I think it is no secret that I have become obsessed with unit testing, more specifically the mockist theory of testing. Unfortunately over the last two weeks I have come across a scenario that JMock does not allow me to test very easily.

The scenario is basically when you instantiate an object inside of a method to preform some action. This problem is only occuring because I am not using a factory to get my objects. If I were using a factory, I could inject a mock factory and all would be wonderful. Now we can debate back and forth whether I should use a factory or not, but at the end of the day it doesn’t matter, because this scenario will occur at some point. Descriptions are a lot harder than code, here is what I am talking about:


public List getPeopleHiredOn(Date hireDate)
{
   PersonSearch search = new PersonSearch();
   return search.by(hireDate);
} 

Just for good measure I will throw in a second scenario:


public void sendInvitationEmail(String address)
{
   InvitationEmail email = new InvitationEmail();
   email.sendTo(address);
   email.send();
} 

Going forward I am going to focus on the first example.

I want to point out that this situation seems to occur in the service layer of my application. The scenario is usually when I am creating a domain object to handle a request from an input to the API. However, I am sure it can occur in other places as well.

JMock gives you many options for manipulating your code, but it doesn’t do you any good unless there is a object you can inject at some point into the process. In this case, I have no such object, because the object is being created within the method I want to test. Enter AspectJ.

With AspectJ I gain the ability to intercept the creation of the PersonSearch object and replace it with a mock. My goal is to have the final test method look like this:


public class PersonServiceTest
{
...
   @Test
   public void shouldExecuteNewPersonSearchWithSuppliedDate()  
   {
      final Date hireDate = new Date();
      final List results = new ArrayList();
      PersonService service = new PersonService();
      
      mockery.checking(new Expectations(){{
         one(personSearch).by(hireDate);
         will(returnValue(results));
      }});
      
      List matchingPeople =  service.getPeopleHiredOn(hireDate);

      assertEqual(matchingPeople, results);
   }
...
}

Assuming that you have an understanding of JMock, the only thing that should look out of place would be that I never declare or initialize personSearch. This is where the magic comes in, I declare the personSearch as static and initialize it in a setup method.


public class PersonServiceTest
{
   private Mockery mockery = new Mockery() {{setImposteriser(ClassImposteriser.INSTANCE);}};
   private static PersonSearch personSearch;

   @Before
   public void resetMocks()
   {
      personSearch = mockery.mock(PersonSearch.class);
   }
...

Because I needed to extend a concrete class, I had to add the setImposteriser to the declaration of my mockery, but that isn’t really important in this example. The only unusual thing I have done is use a setup method to create my mocks instead of doing them inline and declaring them as static. I will get to the static declaration in a second.

The next step is to create our Aspect. In AOP, an Aspect is a collection of pointcuts and advise that you want to occur in your program. My Aspect looks like this:


public class PersonServiceTest
{
...

   @Aspect
   static class TestAspects
   {
      @Pointcut("call(com.danielroop.personservice.PersonSearch.new(..))")
      public void interceptPersonSearchConstructor(){}

      @Around("interceptPersonSearchConstructor()")
      public PersonSearch interceptConstructorForPersonSearch()
      {
         return personSearch;
      }
   }
}

The first thing to note, is that I used the AspectJ 5 annotation support. Next you should notice that I created this as a sub class of my test class. On a side note, I attempted making the test case an Aspect but I ran into conflicts between the JUnit and AspectJ @Before and @After annotations, because apparently AspectJ doesn’t check the type of annotation, just the name. Anyways, this is where the static personSearch is important. In order to create a subclass Aspect like I did here, you have to make it a static class, so in order to access personSearch from the parent you must make it static. I am not sure why the Aspect needs to be static, but the compiler told me so, so I didn’t argue.

That is it, at this point whenever new PersonSearch() gets called any where in this test case it will replace it with a version of my mock PersonSearch, that I can then set expectations on in my test method.

I am a little disappointed with a few of the hoops I needed to jump through, and wish that JMock incorporated Aspects into their expectations, but for now we have to make due with the tools we have.

The final class would look like this:


public class PersonServiceTest
{
   private Mockery mockery = new Mockery() {{setImposteriser(ClassImposteriser.INSTANCE);}};
   private static PersonSearch personSearch;

   @Before
   public void resetMocks()
   {
      personSearch = mockery.mock(PersonSearch.class);
   }

   @Test
   public void shouldExecuteNewPersonSearchWithSuppliedDate()  
   {
      final Date hireDate = new Date();
      final List results = new ArrayList();
      PersonService service = new PersonService();
      
      mockery.checking(new Expectations(){{
         one(personSearch).by(hireDate);
         will(returnValue(results));
      }});
      
      List matchingPeople =  service.getPeopleHiredOn(hireDate);

      assertEqual(matchingPeople, results);
   }

   @Aspect
   static class TestAspects
   {
      @Pointcut("call(com.danielroop.personservice.PersonSearch.new(..))")
      public void interceptPersonSearchConstructor(){}

      @Around("interceptPersonSearchConstructor()")
      public PersonSearch interceptConstructorForPersonSearch()
      {
         return personSearch;
      }
   }
}
This entry was posted in testing and tagged , , , , . Bookmark the permalink.

15 Responses to JMock and AspectJ

  1. Maxim Porges says:

    Are you mocking me? Seriously, thanks for the post, I’m going to read it later when I get a chance.

    You should totally write a unit testing framework for ROOP, that writes itself using AI. Remember that in Soviet Russia, CODE FORMATS YOU! :)

  2. Daniel Roop says:

    @Max

    My goal is to mock out every layer that I interact with. So at work, I guess that would be my BA, Manager, and Director..so yeah…I am planning on mocking you ;-).

  3. Maxim Porges says:

    Excellent. I have a feeling that the mock version of myself will be a lot smarter and consume fewer resources than the actual me.

  4. Neha says:

    Hi..thanks for this post….i have exactly the same problem at hand about using aspectj for objects initilialized in methods..but when i used your example, there are compilation errors coming because the compiler is unable to recognize annotations @aspect and all..please tell me how did u run the unit test?

  5. Daniel Roop says:

    @Neha

    If you are using eclipse you can just get the aspectj plugin. After you download the project you can configure your eclipse java project to be an aspectj project. This will add all the dependencies you need for compiling and when you run your unit tests you should be fine.

    If you are using something other than eclipse, you will need to download aspectj and use their compiler. I have never done this before outside of maven or ant, so I am not sure how to do it form the command line, but both maven and ant have plugins for compiling projects using aspectj.

    Hope this helps.

  6. Neha says:

    I am able to run the test case now by following ur suggesstions..but still my class is not being mocked…also the compiler is asking for an argument like @Before(argNames=””) for the Before annnotation..what can I do next..Thanks..

  7. Neha says:

    Are u using any load time weaving for running this test case or specifying any VM arguments?

  8. Neha says:

    Actually i was mistakenly using aspectj’s before instead of junit’s. So now its running fine…but its still unable to replace the mock object at runtime…

  9. Neha says:

    This is the code for your reference…

    public class MockTest1 {

    private Mockery mockery = new Mockery() {{setImposteriser(ClassImposteriser.INSTANCE);}};
    private static IPMSysConfig sysConfigMock;

    @Before
    public void resetMocks()
    {
    sysConfigMock = mockery.mock(IPMSysConfig.class);
    }
    @Test
    public void testObserverName() {

    final IPolicyExe policyexeMock = mockery.mock(IPolicyExe.class);
    //sysConfigMock = mockery.mock(IPMSysConfig.class);
    final AttrValueListSet set = new AttrValueListSet();
    //set.addAttrList();
    AttrValueList vallist = new AttrValueList();
    // expectations
    mockery.checking(new Expectations() {{
    one (policyexeMock).getCtrlParamByName(“PollProtocol”);
    one (policyexeMock).getCtrlParamByName(“DBModel”);
    one (policyexeMock).getCtrlParamByName(“FileLogModel”);
    one (policyexeMock).getCtrlParamByName(“AMOModel”);
    one (policyexeMock).getPolicyName();
    one (policyexeMock).getSystemConfigAttrValueListSet();will(returnValue(set));

    }});
    mockery.checking(new Expectations(){{
    one(sysConfigMock).refresh();
    one (sysConfigMock).setNxSystem();
    }});
    try {

    IPM_PollingManagement myTestClass = new IPM_PollingManagement(policyexeMock);
    } catch (Exception e) {
    // TODO Auto-generated catch block

    }
    }
    @Aspect
    static class TestAspects
    {
    @Pointcut(“call(com.agilent.netexpert.ipmanager.dnmp.sharedobj.IPMSysConfig.new(..))”)
    public void interceptIPMSysConfigConstructor(){}

    @Around(“interceptIPMSysConfigConstructor()”)
    public IPMSysConfig interceptConstructorForIPMSysConfig()
    {
    return sysConfigMock;
    }
    }

    }

  10. Daniel Roop says:

    @Neha

    I looked at your test class, and I can’t be positive, but based on your comments I would check your imports.

    “also the compiler is asking for an argument like @Before(argNames=””) for the Before annnotation.”

    The @Before annotation from junit should not require any argNames, the AspectJ @Before annotation might. Makes sure you are importing the org.junit.Before class not the
    import org.aspectj.lang.annotation.Before class.

    If that doesn’t work let me know.

  11. Neha says:

    Hi Daniel..
    Yes the annotation problem is solved now and its not giving any compilation problems…i have run the test case and yet the object IPMSysConfig is not getting mocked..its calling the constructor of the actual class..Is something missing in my code? Do I have to configure it in any way?

  12. Daniel Roop says:

    @Neha

    I just reimplemented my example to make sure I wasn’t forgetting anything so here is what I have for you.

    1. I am using eclipse with the aspectj plugin. I didn’t need to do anything except convert the project to an aspectj project, and include all the dependencies for jmock in the classpath.

    2. While reimplementing I only had to change one thing from the code I uploaded. I used a different namespace so I had to update my pointcut. You didn’t include any packages so I can’t check this for you, but I would double check your spelling in the pointcut declaration. This will not give you any IDE or compiler feedback if you have it typed incorrectly.

    3. Even though I am using the aspectj plugin I am pretty confident it is doing compiling time weaving, so if you are doing it from a command line you may want to keep that in mind.

    hope this helps. If you need any more tips maybe contact me offline, with the actual source code,s o I can attempt to compile it myself and run it.

  13. Neha says:

    Hi Daniel, Lately I stumbled upon JEasyTest plugin of eclipse, which does pretty much the same thing…and its much simpler..I dont need to worry about AspectJ part of it as I am finding it quite complex..have you used this plugin? https://jeasytest.dev.java.net/

  14. Daniel Roop says:

    @Neha

    Sorry you couldn’t get AsjpectJ working for you. Good luck with jeasytest. I hadn’t seen it before so thanks for the link.

  15. Neha says:

    Hi Daniel!

    Hope u r doing fine…I am stuck with my JEasyTest approach now…I stumbled on an aspectj compiler bug which does not let me automate my tests….I need more help on this…would be really grateful if you could help me use the example in this blog entry..my mail id is nehagiri@gmail.com..please drop me a mail on it so that I can communicate with u..thanx

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>