Skip to main content

Spring, lets Mock!

Posted by gadominas on April 7, 2011 at 2:45 PM PDT

How would you mock your services which are not mock-friendly?
Let’s introduce a real case (the bad one) with some preconditions:

  • You have a service where DAOs are wired at runtime based on the some conditions.
  • DAOs lookup API is private and not exposed to the caller

In this case you will be blocked from mocking those DAOs because of such approach.
The solution I came up with was to tweak the Spring context by unregistering the target bean (service/dao or whatever), creating the mock and registering to the context back by aliasing, to make it retrievable as regular target bean. The concept code snippet from my routine by mocking DAO’s would be:

protected > void mockDAO(Class mockDaoClass) {
        String mockedDaoClassName = mockDaoClass.getName();
        GenericApplicationContext appContext = (GenericApplicationContext) applicationContext;
        DefaultListableBeanFactory defaultBeanFactory = ((DefaultListableBeanFactory) appContext.getBeanFactory());

        T originalDAO = (T) appContext.getBean(mockedDaoClassName);
        T mockedDao = EasyMock.createNiceMock(mockDaoClass);

        try {
            appContext.removeBeanDefinition(mockedDaoClassName);
            defaultBeanFactory.registerSingleton(mockedDaoClassName + DAO_ALIAS_ORIGINAL, originalDAO);
            defaultBeanFactory.registerSingleton(mockedDaoClassName + DAO_ALIAS_MOCKED, mockedDao);
        } catch (Exception e) {
            fail(e.getMessage());
        }

        defaultBeanFactory.registerAlias(mockedDaoClassName + DAO_ALIAS_MOCKED, mockedDaoClassName);
        mockedDAOGroup.add(mockedDaoClassName);
    }

And after you finish, restore the bean alias to the original one:

@After
public void revertMockedDAOInAppContext() {
       for (String mockedDAOClassName : mockedDAOGroup) {
          GenericApplicationContext appContext = (GenericApplicationContext) applicationContext;
          DefaultListableBeanFactory defaultBeanFactory = ((DefaultListableBeanFactory) appContext
                        .getBeanFactory());

          defaultBeanFactory.registerAlias(mockedDAOClassName + DAO_ALIAS_ORIGINAL, mockedDAOClassName);
      }

          mockedDAOGroup.clear();
    }

Yet another solution is to wrap the application context and override the #getBean operation. >> Check this out too <<.

Have a better solution?

Related Topics >>