Spring Integration Tests, Part I, Creating Mock Objects

When writing integration tests with Spring, it can sometimes be convenient to mock one or more of Spring bean dependencies. However, during some circumstances strange things may happen…

(In this post, Mockito has been used for creating mock objects, but the same problem applies to EasyMock as well. You can find the corresponding files if you follow the provided links.)

Test setup

In the following example the SomeClass needs a reference to an instance of the SomeDependency interface:

When writing the integration test, we use the static Mockito.mock(Class classToMock) method to create a mock instance. The straightforward approach would be to let Spring instantiate the mock object with a static factory method in the xml configuration, e.g.


(Corresponding EasyMock config.)

To verify that the beans are created and wired correctly, we write a simple test:


(Same test for EasyMock, just update the @ContextConfiguration with the EasyMock config file.)

To our surprise, the test fails:

Explanation

In order to find a solution to the problem, we need to understand Spring's initialization process:

  1. Load bean definitions.
    This step includes scanning the application context XML files, scanning packages defined by component-scan, and loading the bean definitions found into the bean factory. (BeanFactoryPostProcessors such as the PropertyPlaceHolderConfigurer may be called to update the bean definitions.)
  2. Instantiate beans and store them in the application context.
    The bean factory creates the bean from the bean definitions. Bean dependencies get injected.
  3. Bean post processing.
    All initializing methods (e.g. annotated with @PostConstruct, init-methods declared in the XML and the afterPropertiesSet() method will execute. Annotations such as @Required will be validated. Dynamic proxies in an AOP environment will be created and so on.

The problem that we see occurs due to a corner case in the first two steps. When the bean definition is created Spring scans through the application context XML file. It finds the declaration of the mock bean and a reflective call is made to find out the return type of the method defined by the factory-method declaration in an attempt to determine the bean's type. The return type of this method is declared as the formal type parameter <T> (remember that we use the Mockito.mock(Class<T> classToMock) as factory method). Consequently, the bean definition of someDependencyMock will be of java.lang.Object and not SomeDependency as one would think.

In the second step, the someClass bean is instantiated. Springs discovers the @Autowired annotation and tries to fetch an existing SomeDependency bean from the application context. None has yet been created, so the bean factory proceeds and looks for suitable bean definition in order to create an instance of the requested bean on the fly. This operation fails because the bean definition of someDependency is mapped to java.lang.Object. At this point, Spring bails out and throws the NoSuchBeanDefinitionException.

FactoryBean to the rescue

The problem is solved by implementing a custom FactoryBean that can be used whenever we need to mock an object:


(In the EasyMockFactoryBean, I have added an enum to define the type of mock object.)

Next, the Spring test config is updated to use the factory bean:


(Corresponding EasyMock config.)

Spring will still instantiate the beans in the order that they were declared in the mockito-test-config.xml, i.e. starting by creating an instance of SomeClass and then attempt to inject an instance of a SomeDependency into it before any has been created. The good news is that bean factory will scan through all declared FactoryBeans, calls its getObjectType() method, and if the returned type is correct, call its getObject() which should return an instance of the requested type.

Alternative solution

There is another more brittle solution to the problem. By simply changing the order of the tags in the initial application context, e.g. putting the bean declaration that defines the mock above the component scan, will cause the test to pass. The bean definition will still be wrong in the sense that the someDependencyMock will be defined as a java.lang.Object, but there is another important difference. In this case, the bean factory will invoke the Mockito.mock(Class<T> classToMock) method with SomeDependency as parameter causing the someDependencyMock bean to be created and subsequently stored in the application context. Later, the someClass bean is created and the dependency can obtained from the application context when requested by the bean factory and the wiring of the beans will succeed. However, making sure that the order of the bean declarations in the XML is always correct may neither be simple nor obvious, and it will get increasingly more difficult the more beans you add to the configuration.

Disclaimer

As stated in the in the first paragraph, I would only consider this solution for integration tests because of performance reasons. The application context itself adds some overhead, but the big performance hit is to create and wire all beans that are associated with it. When writing unit tests there is no need for this, so you could revert to constructor or setter dependency injection, Spring's ReflectionTestUtils.setField(), PowerMock's Whitebox.setInternalState(), Mockito's @InjectMocks or similar techniques.

Acknowledgements

Thanks to Wilhelm Kleu at stack overflow for suggesting the solution and to Mattias Hellborg Arthursson for valuable discussions.

Dependencies

  • Spring 3.0.6
  • Mockito 1.8.5
  • jUnit 4.10
  • EasyMock 3.1

References

Next post: Spring Integration Tests, Part II, Using Mock Objects

Edit

As of Spring 3.2 RC1, the problem with generic factory methods and mock object has been solved.

11 Comments

  1. amertum

    @Qualifier annotation could also be used instead of just the matching type :

    @Autowired
    @Qualifier(“someDependencyMock”)
    SomeClass someClass;

  2. @amertum

    That is correct, because the @Autowired autowires default by type, so @Qualifier can be used to specify a specific bean.

    There are also alternative non-Spring specific solutions that can be used, such as the JSR-250 @Resource annotation, or the JSR-330 @Inject and the @Named annotations.

  3. dan

    Awesome!

    • @hdave: Thanks for the reference. I have not used Springockito myself (it seems like the 1.0.0 version was released shortly after I wrote this post), but I guess that we are trying to solve the same problem. Moreover, the MockitoFactoryBean and the EasyMockFactoryBean described above can be considered obsolete if you use Spring version 3.2.RC1 or later, see Spring Framework 3.2 RC1: New Testing Features. Consequently, it is possible to call the Mockito.mock(...) from a factory-method in the application context xml as i first attempted without any problem.

  4. Vikky

    Can we change the value inside mocked object created by FactoryBean?
    I tried to change it..but no luck.

    Can you please help me out.

    • @Vikky: Once created, the mock object is no different from any other object created by Mockito, i.e. you change the behaviour of the mock object by adding when() methods. Say for example that the SomeDependency interface above has a getNumber() method that returns an int, then you can mock the dependency like:

      I suggest that you read the Mockito manual, especially the Stubbing part.
      Since the mock object will be managed by a Spring application context, it is important that it is reset between different tests, see my blog about Spring Integration Tests, Part II, Using Mock Objects.

    • @hlex: Thank you for your comment. The problem that was solved in the link that you provided is how Mockito can be used in order to create and autowire mock objects into a Spring bean without creating an application context. I wrote a similar example some time ago. The problem that this blog post intends to solve is when you would like to create an application context that contains Spring beans created by using factory methods provided by mock frameworks. There were some quirks using Spring versions < = 3.1 (hence the blog post). However, ass of Spring 3.2, the solution above is redundant.

Trackbacks for this post

  1. Spring Integration Tests, Part II, Using Mock Objects | Jayway Team Blog - Sharing Experience
  2. Mockito, JUnit and Spring | Ask Programming & Technology

Leave a Reply