Blogs

SpringSource Blog

Spring 3.1 M2: Testing with @Configuration Classes and Profiles

Sam Brannen

As Jürgen Höller mentioned in his post announcing the release of Spring 3.1 M2, the Spring TestContext Framework(*) has been overhauled to provide first-class testing support for @Configuration classes and environment profiles.

In this post I'll first walk you through some examples that demonstrate these new testing features. I'll then cover some of the new extension points in the TestContext framework that make these new features possible.

      Please note: this is a cross post from my company blog www.swiftmind.com.

Background

In Spring 2.5 we introduced the Spring TestContext Framework which provides annotation-driven integration testing support that can be used with JUnit or TestNG. The examples in this blog will focus on JUnit-based tests, but all features used here apply to TestNG as well.

At its core, the TestContext framework allows you to annotate test classes with @ContextConfiguration to specify which configuration files to use to load the ApplicationContext for your test. By default the ApplicationContext is loaded using the GenericXmlContextLoader which loads a context from XML Spring configuration files. You can then access beans from the ApplicationContext by annotating fields in your test class with @Autowired, @Resource, or @Inject.

Spring 3.0 introduced support for Java-based configuration via @Configuration classes, but the TestContext framework did not supply an appropriate ContextLoader to support @Configuration classes in tests until now. Spring 3.1 M2 introduces a new AnnotationConfigContextLoader for this purpose, and the @ContextConfiguration annotation has been updated to support declaration of @Configuration classes via a new classes attribute.

Let's take a look at some examples now.

Integration Testing with XML-based Configuration

The Testing chapter of the Spring Reference Manual provides numerous examples of how to configure integration tests using XML configuration files, but we'll include an example here as a quick introduction.

If you're already familiar with the Spring TestContext Framework, feel free to skip to the next section.

<?xml version="1.0" encoding="UTF-8"?>
<beans ...>

    <!-- this bean will be injected into the OrderServiceTest class -->
    <bean id="orderService" class="com.example.OrderServiceImpl">
        <!-- set properties, etc. -->
    </bean>

    <!-- other beans -->

</beans>
package com.example;

@RunWith(SpringJUnit4ClassRunner.class)
// ApplicationContext will be loaded from "classpath:/com/example/OrderServiceTest-context.xml"
@ContextConfiguration
public class OrderServiceTest {

    @Autowired
    private OrderService orderService;

    @Test
    public void testOrderService() {
        // test the orderService
    }
}

In the preceding example we configure JUnit to use the SpringJUnit4ClassRunner to run our tests. We do this using JUnit's @RunWith annotation. We also annotate our test class with Spring's @ContextConfiguration annotation without specifying any attributes. In this case the default GenericXmlContextLoader will be used, and following the principle of convention over configuration Spring will load our ApplicationContext from classpath:/com/example/OrderServiceTest-context.xml. Within the testOrderService() method we can directly test the OrderService that was injected into our test instance using @Autowired. Note that the orderService is defined as a bean in OrderServiceTest-context.xml.

Integration Testing with @Configuration Classes

Spring 3.1 M2's support for integration testing with @Configuration classes is analogous to the XML-based example above. So let's rework that example to use a @Configuration class and the new AnnotationConfigContextLoader.

package com.example;

@RunWith(SpringJUnit4ClassRunner.class)
// ApplicationContext will be loaded from the static inner ContextConfiguration class
@ContextConfiguration(loader=AnnotationConfigContextLoader.class)
public class OrderServiceTest {

    @Configuration
    static class ContextConfiguration {

        // this bean will be injected into the OrderServiceTest class
        @Bean
        public OrderService orderService() {
            OrderService orderService = new OrderServiceImpl();
            // set properties, etc.
            return orderService;
        }
    }

    @Autowired
    private OrderService orderService;

    @Test
    public void testOrderService() {
        // test the orderService
    }
}

There are a few notable differences between this example and the XML-based one:

  1. There is no XML file.
  2. The bean definitions have been converted from XML to Java using @Configuration and @Bean in the static inner ContextConfiguration class.
  3. The AnnotationConfigContextLoader has been specified via the loader attribute of @ContextConfiguration.

Otherwise, the configuration and implementation of the test remain unchanged.

So, how does Spring know to use the static inner ContextConfiguration class to load the ApplicationContext? The answer is convention over configuration. By default, if no classes are explicitly declared, AnnotationConfigContextLoader will look for a static inner class of the test class named ContextConfiguration. Per the requirements of @Configuration classes, this static inner class must be non-final and non-private.

Note: as of Spring 3.1 M2, the default configuration class must be named exactly ContextConfiguration. As of Spring 3.1 RC1, however, the naming restriction has been lifted. In other words, from RC1 forward, you can choose to name your default configuration class whatever you want, but the other requirements still apply.

In the following example we'll see how to declare explicit configuration classes.

package com.example;

@Configuration
public class OrderServiceConfig {

    // this bean will be injected into the OrderServiceTest class
    @Bean
    public OrderService orderService() {
        OrderService orderService = new OrderServiceImpl();
        // set properties, etc.
        return orderService;
    }
}
package com.example;

@RunWith(SpringJUnit4ClassRunner.class)
// ApplicationContext will be loaded from the OrderServiceConfig class
@ContextConfiguration(classes=OrderServiceConfig.class, loader=AnnotationConfigContextLoader.class)
public class OrderServiceTest {

    @Autowired
    private OrderService orderService;

    @Test
    public void testOrderService() {
        // test the orderService
    }
}

We have now extracted the static inner ContextConfiguration class into a top-level class named OrderServiceConfig. To instruct the AnnotationConfigContextLoader to use this configuration class instead of relying on the default, we simply declare OrderServiceConfig.class via the new classes attribute of @ContextConfiguration. As with @ContextConfiguration's locations attribute for resource locations, we can declare multiple configuration classes by supplying a Class[] array to the classes attribute — for example: @ContextConfiguration(classes={Config1.class, Config2.class}, ... ).

This ends the coverage of integration testing with @Configuration classes. Now let's take a look at Spring's testing support for environment profiles.

Integration Testing with Environment Profiles

As Chris Beams discussed in his release announcement for Spring 3.1 M1 and his follow-up blog Introducing @Profile, Spring 3.1 introduces first-class support in the framework for the notion of environments and profiles (a.k.a., bean definition profiles). As of Spring 3.1 M2, integration tests can also be configured to activate particular bean definition profiles for various testing scenarios. This is achieved by annotating a test class with the new @ActiveProfiles annotation and supplying a list of profiles that should be activated when loading the ApplicationContext for the test.

Note: @ActiveProfiles may be used with any implementation of the new SmartContextLoader SPI (see later discussion), but @ActiveProfiles is not supported with implementations of the simpler ContextLoader SPI.

Let's take a look at some examples with XML configuration and @Configuration classes.

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:jee="http://www.springframework.org/schema/jee"
	xsi:schemaLocation="...">

	<bean id="transferService" class="com.bank.service.internal.DefaultTransferService">
		<constructor-arg ref="accountRepository"/>
		<constructor-arg ref="feePolicy"/>
	</bean>

	<bean id="accountRepository" class="com.bank.repository.internal.JdbcAccountRepository">
		<constructor-arg ref="dataSource"/>
	</bean>

	<bean id="feePolicy" class="com.bank.service.internal.ZeroFeePolicy"/>

	<beans profile="dev">
		<jdbc:embedded-database id="dataSource">
			<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
			<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
		</jdbc:embedded-database>
	</beans>

	<beans profile="production">
		<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
	</beans>

</beans>
package com.bank.service;

@RunWith(SpringJUnit4ClassRunner.class)
// ApplicationContext will be loaded from "classpath:/app-config.xml"
@ContextConfiguration("/app-config.xml")
@ActiveProfiles("dev")
public class TransferServiceTest {

    @Autowired
    private TransferService transferService;

    @Test
    public void testTransferService() {
        // test the transferService
    }
}

When TransferServiceTest is run, its ApplicationContext will be loaded from the app-config.xml configuration file in the root of the classpath. If you inspect app-config.xml you'll notice that the accountRepository bean has a dependency on a dataSource bean; however, dataSource is not defined as a top-level bean. Instead, dataSource is defined twice: once in the production profile and once in the dev profile.

By annotating TransferServiceTest with @ActiveProfiles("dev") we instruct the Spring TestContext Framework to load the ApplicationContext with the active profiles set to {"dev"}. As a result, an embedded database will be created, and the accountRepository bean will be wired with a reference to the development DataSource. And that's likely what we want in an integration test!

The following code listings demonstrate how to implement the same configuration and integration test but using @Configuration classes instead of XML.

@Configuration
@Profile("dev")
public class StandaloneDataConfig {

	@Bean
	public DataSource dataSource() {
		return new EmbeddedDatabaseBuilder()
			.setType(EmbeddedDatabaseType.HSQL)
			.addScript("classpath:com/bank/config/sql/schema.sql")
			.addScript("classpath:com/bank/config/sql/test-data.sql")
			.build();
	}
}
@Configuration
@Profile("production")
public class JndiDataConfig {

	@Bean
	public DataSource dataSource() throws Exception {
		Context ctx = new InitialContext();
		return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
	}
}
@Configuration
public class TransferServiceConfig {

	@Autowired DataSource dataSource;

	@Bean
	public TransferService transferService() {
		return new DefaultTransferService(accountRepository(), feePolicy());
	}

	@Bean
	public AccountRepository accountRepository() {
		return new JdbcAccountRepository(dataSource);
	}

	@Bean
	public FeePolicy feePolicy() {
		return new ZeroFeePolicy();
	}
}
package com.bank.service;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader=AnnotationConfigContextLoader.class,
    classes={TransferServiceConfig.class, StandaloneDataConfig.class, JndiDataConfig.class})
@ActiveProfiles("dev")
public class TransferServiceTest {

    @Autowired
    private TransferService transferService;

    @Test
    public void testTransferService() {
        // test the transferService
    }
}

In this variation, we have split the XML configuration into three independent @Configuration classes:

  • TransferServiceConfig: acquires a dataSource via dependency injection using @Autowired
  • StandaloneDataConfig: defines a dataSource for an embedded database suitable for developer tests
  • JndiDataConfig: defines a dataSource that is retrieved from JNDI in a production environment

As with the XML-based configuration example, we still annotate TransferServiceTest with @ActiveProfiles("dev"), but this time we specify the AnnotationConfigContextLoader and all three configuration classes via the @ContextConfiguration annotation. The body of the test class itself remains completely unchanged.

For details on how to simplify the above @Configuration classes consult the Spring 3.1 M1: Introducing @Profile blog post.

ApplicationContext Caching

Since Spring 2.5 the Spring TestContext Framework has cached ApplicationContexts for integration tests based on a key that was generated from all merged context resource locations for a given test. Since the ContextLoader SPI only supported locations, this key generation algorithm was sufficient for uniquely identifying the configuration used to load an ApplicationContext. With the added support for configuration classes and profiles, however, the old algorithm is no longer adequate.

As a result, the context cache key generation algorithm has been updated in Spring 3.1 M2 to include the all of the following:

  • locations (from @ContextConfiguration)
  • classes (from @ContextConfiguration)
  • contextLoader (from @ContextConfiguration)
  • activeProfiles (from @ActiveProfiles)

What this means for you as a developer is that you can implement a base test class that declares a certain set of resource locations or configuration classes. Then, if you want to run tests against that base configuration but with different active profiles, you can extend that base test class and annotate each concrete subclass with @ActiveProfiles, supplying a different set of profiles to activate per subclass. Each of these subclasses would therefore define a unique set of configuration attributes that would result in different ApplicationContexts being loaded and cached.

SmartContextLoader Supersedes ContextLoader SPI

As hinted at earlier in this post, Spring 3.1 M2 introduces a new SmartContextLoader SPI that supersedes the existing ContextLoader SPI. If you plan to develop or already have developed your own custom ContextLoader, you will likely want to take a closer look at the new SmartContextLoader interface. In contrast to the old ContextLoader interface, a SmartContextLoader can process both resource locations and configuration classes. Furthermore, a SmartContextLoader can set active bean definition profiles in the context that it loads.

ContextLoader will continue to be supported, and any existing implementations of that SPI should continue to work as is; however, if you want to support configuration classes or environment profiles in your custom loader, you will need to implement SmartContextLoader.

DelegatingSmartContextLoader

If you have been paying close attention to the examples presented thus far, you may have noticed that we always had to explicitly declare AnnotationConfigContextLoader.class for @ContextConfiguration's loader attribute when using configuration classes. But when we specified XML configuration files (or relied on convention over configuration), the GenericXmlContextLoader was used by default.

Wouldn't it be nice if Spring could just notice whether we are using configuration classes or XML resource locations and then automatically pick the right ContextLoader to load our application context?

Yeah, we think so, too! ;)

So for Spring 3.1 RC1 we plan to introduce a DelegatingSmartContextLoader that will delegate to a list of candidate SmartContextLoaders (i.e., GenericXmlContextLoader and AnnotationConfigContextLoader) to determine which context loader is appropriate for a given test class's configuration. The winning candidate will then be used to actually load the context.

Once this work is complete, DelegatingSmartContextLoader will replace GenericXmlContextLoader as the default loader. Feel free to follow the progress of this development in JIRA: SPR-8387.

Summary

Spring 3.1 provides first-class testing support for @Configuration classes and environment profiles, and we encourage you to try out these features as soon as you can. M2 is the last milestone in the 3.1 release train. So if you find any bugs or have any suggestions for improvements, now is the time to take action!


(*) The reference manual has not yet been updated to reflect testing support for @Configuration classes and environment profiles, but these features will certainly be well documented by Spring 3.1 RC1 or GA. In the meantime, the JavaDoc for each of the new classes and annotations can serve as a good starting point.

Similar Posts

Share this Post
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • DZone
  • LinkedIn
  • Slashdot
  • Technorati
  • Twitter
 

38 responses


  1. Great article. This should lessen or eliminate my XML configuration files during testing.


  2. Nice article, thanks for the information.


  3. This looks great! Particularly like the use of active profiles – that would make our deployment process so much easier.

    Can Spring magic up any spare time from somewhere for me to implement this in our application?
    ;)

    James


  4. We have been using @ContextConfiguration with its locations attribute to refer to specific XML configurations in the past. Now, I was hoping that this approach would give me more control over my tests. Unfortunately, we rely on domain object injection using compile-time spring-aspects. While the XML approach worked, the AnnotationConfigContextLoader does not inject the services created into any domain objects constructed within the test class. The injection of the service into the test class itself works, though. Any hints?


  5. OK, I figured, the root cause for the problem is that I am using Mockito to create the mocked service (thus using CGLIB). While in the XML style configuration I can explicitly declare the required type, in the latter case it tries to guess the type and thus does not work properly.


  6. Hi Maverick,

    I didn't totally understand the issue you're facing without seeing your configuration and test class, but in any case, using configuration classes generally should not restrict your ability to use Mockito to mock an interface.

    Did I understand you correctly? Did you resolve your problem?

    Sam


  7. We are testing classes that depend on domain objection injection, i.e. using compile-time AspectJ. As such, we may have classes that we want to test, that look like the following:

    @Configurable ( autowire = Autowire.BY_TYPE )
    public class MyDomainObject {

    private MyService service;

    public void setService ( MyService ms ) {
    this.service = ms;
    }

    public MyDomainObject () {
    } // aspect follows here
    }

    In order to mock the service interface (MyService), we use an XML configuration similar to this one:

    This is then pulled into the test class using @ContextConfiguration:

    @ContextConfiguration ( "/myMockedService.xml" )
    public class MyTest {

    @Autowired
    MyService mockedService;

    @Test
    public void verifyDomainObject () {
    when ( mockedService.bla () ). thenReturn ( 42 );

    MyDomainObject cut new MyDomainObject ();
    cut.testSomeBehaviour ();
    }
    }

    This works all fine, but when I try to switch to class-based @ContextConfiguration, as long as I don't use class-based @ContextConfiguration. My @Configuration class simply calls Mockito.mock(MyService.class) in this case. The strange thing is, that the mocked service is properly injected into the test class, but not the domain object.

    I debugged the AnnotationBeanConfigurerAspect. In the case of class-based configuration, it has no BeanFactory set, but I was unable to figure out why.


  8. @Maverick. You've just reminded me of a problem I had with autowiring the other day. My tests were working fine, but autowiring wasn't happening in the app itself. This was because I'd left out , but in the tests, it gets added for you by Spring Test.

    @Sam. Dave Syer and I were discussing the idea of using a child context for wiring the test fixture (and only adding the additional listeners there), and keeping the parent one purely as specified for the application.
    If I understand what you are saying about the context caching, I think we'd be okay with that as we'd use your caching for the head (assuming time and motivation get found to give it a go) ?


  9. So, is this really a bug I am facing, or am I doing something completely wrong?


  10. We have been using @ContextConfiguration with its locations attribute to refer to specific XML configurations in the past. Now, I was hoping that this approach would give me more control over my tests. Unfortunately, we rely on domain object injection using compile-time spring-aspects. While the XML approach worked, the AnnotationConfigContextLoader does not inject the services created into any domain objects constructed within the test class. The injection of the service into the test class itself works, though. Any hints?


  11. Hi Maverick,

    It sounds more like a configuration issue than a bug.

    Unfortunately, your XML configuration doesn't get displayed in the blog comments. So I can't really see how you were configuring your application context with XML.

    Please note that Spring 3.1 provides special @Configuration class support for configuring AspectJ load-time weaving via the new @EnableLoadTimeWeaving annotation.

    If you're still having issues with AspectJ and @Configuration classes when using AnnotationConfigContextLoader, please open a JIRA issue and assign it to me (sbrannen) so that I can take a closer look at your problem. Also, please attach a minimal project to the JIRA issue that demonstrates the problem you're encountering.

    Thanks,

    Sam


  12. @Neal: I assume you already noticed that I resolved the JIRA issue (SPR-8503) that you opened as a duplicate.

    For anyone else interested in this topic, please watch JIRA issue SPR-4632 which is currently schedule for Spring 3.2.

    Regards,

    Sam


  13. @Sam. Yes. Saw it's a dupl. I'm usually pretty good at avoiding them :)


  14. Sorry for the missing XML, here it comes:
    <beans>
    <context:spring-configured/>

    <bean id="myMockedService" class="org.mockito.Mockito" factory-method="mock" primary="true">
    <constructor-arg value="MyService" />
    </bean>
    </beans>
    Anyway, I think the problem is the missing context:spring-configured in Java. As https://jira.springsource.org/browse/SPR-7888 suggests, the Java config way behaves differently and I think that's the reason, why my domain objects don't get their services. Apart from that, we're using compile-time weaving, so load-time weaving will not solve the problem. I also debugged a little, so I am pretty sure, the aspect is triggered, so it's woven, but it simply won't find the context or the service generated by the @Configuration inner class.


  15. Maverick,

    Thanks for researching this and finding SPR-7888. That seems to hit the nail on the head for the issue you're encountering.

    @SpringConfigured would do the trick for @Configuration classes, but it has not been implemented yet and unfortunately isn't scheduled to be supported until Spring 3.2.

    For further discussions on this topic, please add comments directly to the JIRA issue.

    Thanks,

    Sam


  16. Too bad, but isn't SPR-7888, as described now, just a workaround. The example shown there gives me the means to perform manual injection on my domain objects, but that could be very tedious, if we have loads of them. Isn't autowiring what we need here? In our example, we may have hundreds of domain objects, each of them accessing individual or no services at all. Wiring these dependencies manually by writing it into code, seems to me like going back to the days before Spring, doesn't it?


  17. @Maverick: I have answered your questions in the comments section for SPR-7888. Please follow up there.


  18. @Sam: I think, I found another problem. After upgrading to 3.1.0.M2, I wanted to play around with the above mentioned ContextConfiguration class. However, I have a test class inheritance, e.g. AbstractBusinessTestContext, which already sets the Spring JUnit runner and loads commonly used XML configuration files. Now, if I extend from this class, and want to use the annotation config loader in addition, it always complains about the missing ContextConfiguration class in AbstractBusinessTestContext. It seems, as if it's looking in the wrong class within the hierarchy.


  19. Maverick,

    You cannot mix XML and @Configuration classes with @ContextConfiguration in a test class hierarchy. This is because a single ContextLoader (or SmartContextLoader) is responsible for loading the application context from all configured resources (i.e., path-based resource locations such as XML, or configuration classes, but not both).

    This behavior makes sense, since your test configuration should be analogous to your production configuration. When you deploy your application you will also have only a single entry point into your configuration.

    So if you want to use XML and @Configuration classes, you will have to pick one as the entry point and that one will have to include or import the other. For example, in XML you can include @Configuration classes in component scanning; whereas, in a @Configuration class you can use @ImportResource to import XML configuration files.

    Regards,

    Sam


  20. Sam,

    good to know, that's the reason, but I do not fully agree with you, in that it doesn't make sense. In our case, the AbstractBusinessTestContext builds up, what you refer to as "production configuration", that is, it refers to all the XML configuration files, as we would in our production application. However, since our application is not simply an enterprise application providing CRUD operations, but rather has to communicate with some sort of remote agent process, we want to use the AbstractBusinessTestContext for these tests as well, WITHOUT actually talking to the remote agent process. In other words, we want to selectively mock this piece of functionality, in this case using @Configuration and @Primary, w/o retyping all the XML configs.

    I don't think, that this represents a scenario, which "makes no sense", do you?

    Apart from that, I didn't look at the framework code, so I cannot tell if it's done on purpose or simply because it is to difficult.


  21. Hi Maverick,

    I agree that it can make sense to mix and match XML and @Configuration classes. All I was saying is that the testing framework does not allow you to declare both via @ContextConfiguration, but this does not mean that you cannot use both.

    I think it bears repeating…

    If you want to use XML and @Configuration classes, you will have to pick one as the entry point and that one will have to include or import the other. For example, in XML you can include @Configuration classes in component scanning; whereas, in a @Configuration class you can use @ImportResource to import XML configuration files.

    The key here is the notion of a single entry point.

    Have you tried this approach?

    If so, if something didn't work as you expected — for example, if you're not able to override beans as you would expect — please raise a JIRA issue with a minimal reproducible use case.

    To address your last point, the testing framework does not support multiple entry points into the configuration since it would be semantically very difficult to define which beans coming from which type of configuration (i.e., XML or @Configuration classes) at which levels in the test class hierarchy should be loaded first, or conversely what should override what.

    I hope this clears things up a bit for you.

    Regards,

    Sam


  22. Very nice enhancements. How does one use mocking with Java Config for certain parts of the code? For example, lets say OrderService depends on PaymentService and I would like to mock Payment service behavior in my test.

    @Configuration
    public class MyConfig {
    
    	@Bean
    	public OrderService orderService() {
    		return new OrderService(paymentService());
    	}
    
    	@Bean
    	protected PaymentService paymentService() {
    		return new PaymentServiceImpl();
    	}
    }
    
    public class OrderServiceTest {
    
    	private PaymentServiceMock mockPaymentService = Mockito.mock(PaymentService.class);
    	private ApplicationContext ctx;
    
    	@Setup
    	public void setUp() {
    		ctx = new AnnotationConfigApplicationContext(MyConfig.class);
    		// How do I substitute my Mock Payment Service here?
    	}
    
    	@Test
    	public void someTest() {
    		Mockito.when(mockPaymentService.doSomething()).thenThrow(new PaymentException());
    
    		OrderService orderService = ctx.get(OrderService.class);
    		orderService.doSomething();
    		// Validations
    	}
    
    	public void someOtherTest() {
    		Mockito.when(mockPaymentService.doSomethingElse()).thenReturn(someObject);
    		// ...
    	}
    }
    

    I am hoping to re-use the mock object for different behavior of the PaymentService while using the Context to obtain the OrderService with the OrderService having within it the substituted mock PaymentService.

    Thanks and very nice post.


  23. @Sam: Sorry, haven't tried yet, but I think I understand what you mean. Nevertheless, it's more typing than I'd expected. Right now, I stick to my original approach of using an additional XML file for each mocked service, since, I can easily combine XML configurations throughout the test class hierarchy. Only thing to remember is to demarcate the mock services as primary candidates for autowiring.


  24. Maverick,

    I would call the approach you're currently taking "bean overloading" since you have multiple beans of the same type but with different names/IDs. For bean overloading you will in fact need to mark one as primary for autowiring by type, unless you use qualifiers.

    However, if you use Spring's support for "bean overriding" (i.e., providing a bean definition with the same name/ID in a subsequent configuration file or configuration class), you won't need to mark the second bean definition (i.e., for the mock) as primary, since "the last one wins" with bean overriding.

    That should make things easier for you, since there's less to remember. Just make sure you name your mocked version of your bean the same as your production version of the bean.

    Make sense?

    Sam


  25. Hi Sanjay,

    How does one use mocking with Java Config for certain parts of the code? For example, lets say OrderService depends on PaymentService and I would like to mock Payment service behavior in my test.

    For starters, there's no need to instantiate an ApplicationContext on your own within your test. That's the job of the Spring TestContext Framework. ;) (see examples above in the blog post)

    So keeping your MyConfig class as it is, you could provide a stand-alone MockConfig class and rewrite your test as follows…

    
    @Configuration
    public class MockConfig {
    
    	@Bean
    	public PaymentService paymentService() {
    		return Mockito.mock(PaymentService.class);
    	}
    }
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes={MyConfig.class, MockConfig.class})
    public class OrderServiceTest {
    
    	@Autowired
    	private OrderService orderService;
    
    	@Autowired
    	private PaymentService paymentService;
    
    	@Test
    	public void someTest() {
    		when(paymentService.doSomething()).thenThrow(new PaymentException());
    
    		orderService.doSomething();
    		// validations
    	}
    }
    
    

    Things to note:

    — I have not declared AnnotationConfigContextLoader.class as the loader, since this is longer necessary as of Spring 3.1 RC1.
    — The paymentService bean declared in MockConfig overrides the bean declared in MyConfig.
    — In the test you can have the mocked PaymentService injected via autowiring and then configure as you normally would within a @Test or @Before method (i.e., not a @Setup method, as that annotation does not exist in JUnit or TestNG).

    Regards,

    Sam


  26. Sam,

    I know roughly about the concept of "bean overriding". However, I personally don't like the term "subsequent", as I usually keep my Spring configs modular in a way, that the order of loading doesn't matter. Hence, I can live well with the approach of bean overloading, since primary candidates in our production configuration are rather rare, if existent at all.

    Anyway, I think the discussion is valuable, as it shows various approaches of reaching the same goal, if not for me in person, maybe for other interested readers ;-)


  27. FYI: for those following the discussions here…

    The solution I suggested to Sanjay is an example of "bean overriding" since the MyConfig and MockConfig configuration classes both define a bean of type PaymentService named "paymentService" (i.e., from the name of the method).

    Thus, when the configuration is loaded for the OrderServiceTest class, the paymentService bean defined in MockConfig overrides the bean of the same name in MyConfig.


  28. Sam,

    the problem with your PaymentService example is, that once the PaymentService is proxied by means of an AOP proxy, e.g. for @Transactional, you won't be able to use @Autowired, as you don't get the mock itself anymore. Thus, you have to use a configuration as inner class, which stores the created mock, aside from Spring autowiring. That was my actual problem, which is why I tried to switch to @Configuration at all.


  29. Sam,

    I have to revise my previous comment. Strangely, using two configuration classes, one for the production configuration with an @ImportResource and one for the overloading with a specific mock, the @Autowired service instance Spring injects me, is no longer proxied transactionally. It seems that only applies to the XML domain imported using the first configuration. The second, test-specific, configuration class is unaffected.

    That's exactly what I have been looking for the whole time.


  30. Sam,

    don't mean to spam this thread, but I have to revise again: the TransactionInterceptor is still there, but I get autowired my Mockito mock, not the Transaction proxy!!!

    Any explanations?


  31. Sam,

    OK, my fault again. The 'when()' stubbing works perfectly fine through the TransactionInterceptor, but not the verification. Complains about the target proxy not being a mock. So the only solution I see, is storing the mock in a static variable from the test configuration to access it as a mock.


  32. @Maverick: I'm glad you worked all that out on your own. ;)

    @ALL: in the example I provided Sanjay, I was assuming that the PaymentService bean would not be advised via AOP (e.g., transactional proxies, etc.). Thus, as Maverick pointed out, if the paymentService that is injected into your test is a JDK dynamic proxy, you may have difficultly with your mocking framework.

    The 'when()' stubbing works perfectly fine through the TransactionInterceptor, but not the verification. Complains about the target proxy not being a mock. So the only solution I see, is storing the mock in a static variable from the test configuration to access it as a mock.

    Another option is to access the target of the dynamic proxy and pass that to the methods of your mocking framework (e.g., instead of passing in the transactional proxy).

    Note that you can access the target object of a Spring AOP proxy in the following generic fashion:

    
    Object target = null;
    
    if (proxy instanceof Advised) {
    	target = ((Advised) proxy).getTargetSource().getTarget();
    }
    
    // do something with the target if it's not still null...
    
    

    Regards,

    Sam


  33. @Sam: When retrieving the target object using "Advised", you should use a recursive loop, since, as far as I know, the target may be proxied more than once.

    Regards,

    Maverick


  34. Hi Sam,
    How do I use a bean that has defined request scope and Scoped Proxy Mode? I get the error detailed below

    Bean definition is like this
    @Bean
    @Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
    public SampleBean getSampleBean() {
    return new SimpleBean();
    }

    and written the test using java configuration as explained in the blog but get this error

    testException = org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.sampleBean': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request., mergedContextConfiguration = [MergedContextConfiguration@578d578


  35. I am having the exact same issue as Susmit. Did you figure this out or does anyone else know how to resolve?


  36. Hi Susmit and Jared,

    This is a known issue and in fact a missing feature in Spring's testing support.

    Support for such a feature has been requested in JIRA issue SPR-4588 (Provide support for session/request scoped beans for integration testing. So feel free to follow the progress of that issue for further details.

    Regards,

    Sam


  37. Hi Maverick,

    You'll be happy to know that there is no longer an issue with mocks that are advised with AOP in the latest versions of Mockito (1.9 and higher).

    Details here: http://code.google.com/p/mockito/issues/detail?id=196

    Regards,

    Sam


  38. Cool. Just used this example when writing a test for the fuzzydb Spring Data bindings: https://github.com/whirlwind-match/fuzzydb-spring/commit/9c06fd62d99211ef9c7f8dd52c79924ee3355447

One trackback

Leave a Reply