Injecting test doubles instead of beans using Spring's XML configuration

In the following recipe, we will replace an existing bean with a test double using Spring's XML configuration.

Getting ready

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>

How to do it...

To write an integration test for the system and replace the bean with a mock, you have to perform the following steps:

  1. Write an integration test that sets the application context of the system under test.
  2. Create an additional XML configuration of your application context.
  3. Override existing beans (IDs have to match) with proper Mockito class factory methods (depending on mock or spy).

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);
    }

}

Note

For TestNG, the only thing that changes is that you do not use the @RunWith(SpringJUnit4ClassRunner.class) annotation but instead, you make the test class extend the AbstractTestNGSpringContextTests class.

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>

Note

If you do not want to execute the real logic of your component, you should use a mock. If however you want to execute the real logic and are only interested in verifying whether interactions took place, then you should use a spy.

How it works...

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]]  
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.11.247