In the following recipe, we will replace an existing bean with a test double using Spring's XML configuration.
Let's assume that our system under test is the tax transferring system for a given person, as shown in the following code:
public class TaxTransferer { private final TaxService taxService; public TaxTransferer(TaxService taxService) { this.taxService = taxService; } public boolean transferTaxFor(Person person) { if (person == null) { return false; } taxService.transferTaxFor(person); return true; } }
As shown in the previous example, TaxService
is a class that will perform a web service call. For readability purposes, we are simulating that we have such data exchanged as follows:
class TaxService { public void transferTaxFor(Person person) { System.out.printf("Calling external web service for person with name [%s]%n", person.getName()); } }
Let's assume that we have an XML-based configuration of the application, as shown in the following code:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <bean id="taxService" class="com.blogspot.toomuchcoding.book.chapter9.InjectingWithSpring.TaxService" /> <bean id="taxTransferer" class="com.blogspot.toomuchcoding.book.chapter9.InjectingWithSpring.TaxTransferer"> <constructor-arg ref="taxService"/> </bean> </beans>
To write an integration test for the system and replace the bean with a mock, you have to perform the following steps:
Let's try to test our system (the example is written for JUnit—for TestNG, refer to the next information box following the snippet. Note that the BDDAssertions
static imports are used—please refer to Chapter 7, Verifying Behavior with Object Matchers, for AssertJ configuration):
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"/chapter9/InjectingWithSpring/application-context.xml","/chapter9/InjectingWithSpring/mock-application-context.xml"}) public class TaxTransfererXmlConfigurationTest { @Autowired TaxTransferer taxTransferer; @Autowired TaxService taxService; @Test public void should_transfer_tax_for_person() { // given Person person = new Person(); // when boolean transferSuccessful = taxTransferer.transferTaxFor(person); // then then(transferSuccessful).isTrue(); verify(taxService).transferTaxFor(person); } }
The following is the additional XML configuration that overrides the bean with a mock:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <bean id="taxService" class="org.mockito.Mockito" factory-method="mock"> <constructor-arg value="com.blogspot.toomuchcoding.book.chapter9.InjectingWithSpring.TaxService"/> </bean> </beans>
The following is the additional XML configuration that overrides the bean with a spy:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <bean id="spiedTaxService" class="com.blogspot.toomuchcoding.book.chapter9.InjectingWithSpring.TaxService" /> <bean id="taxService" class="org.mockito.Mockito" factory-method="spy"> <constructor-arg ref="spiedTaxService"/> </bean> </beans>
We will not go deep into details of how Spring works internally. What is worth mentioning is that by providing the context configuration with the production XML configuration and the test XML configuration, we are able to override bean definitions, as shown in the following code:
@ContextConfiguration(locations = {"/chapter9/InjectingWithSpring/application-context.xml","/chapter9/InjectingWithSpring/mock-application-context.xml"})
In the logs, you will then see the following message:
INFO: Overriding bean definition for bean 'taxService': replacing [Generic bean: class [com.blogspot.toomuchcoding.book.chapter9.InjectingWithSpring.TaxService]; … cropped for readability purposes... defined in class path resource [chapter9/InjectingWithSpring/application-context.xml]] with [Generic bean: class [org.mockito.Mockito]; … cropped for redability purposes... factoryMethodName=mock; defined in class path resource [chapter9/InjectingWithSpring/mock-application-context.xml]]
18.117.11.247