Tag Archives: Integration Testing

mock Spring bean, encapsulate spring bean

How to mock Spring bean without Springockito

NEW EDIT: As of Spring Boot 1.4.0, faking of Spring Beans is supported natively via annotation @MockBean. Read Spring Boot docs for more info.

OLD EDIT: Here is better example how to mock Spring bean.

I work with Spring several years. But I was always frustrated with how messy can XML configuration become. As various annotations and possibilities of Java configuration were popping up, I started to enjoy programming with Spring. That is why I strongly entourage using Java configuration. In my opinion, XML configuration is suitable only when you need to have visualized Spring Integration or Spring Batch flow. Hopefully Spring Tool Suite will be able to visualize Java configurations for these frameworks also.

One of the nasty aspects of XML configuration is that it often leads to huge XML configuration files. Developers therefore often create test context configuration for integration testing. But what is the purpose of integration testing, when there isn’t production wiring tested? Such integration test has very little value. So I was always trying to design my production contexts in testable fashion.

I except that when you are creating new project / module you would avoid XML configuration as much as possible.  So with Java configuration you can create Spring configuration per module / package and scan them in main context (@Configuration is also candidate for component scanning). This way you can naturally create islands Spring beans. These islands can be easily tested in isolation.

But I have to admit that it’s not always possible to test production Java configuration as is. Rarely you need to amend behavior or spy on certain beans. There is library for it called Springockito (EDIT: Springokito link dissapeared). To be honest I didn’t use it so far, because I always try to design Spring configuration to avoid need for mocking. Looking at Springockito pace of development (EDIT: Springokito link dissapeared) and number of open issues  (EDIT: Springokito link dissapeared), I would be little bit worried to introduce it into my test suite stack. Fact that last release was done before Spring 4 release brings up questions like “Is it possible to easily integrate it with Spring 4?”. I don’t know, because I didn’t try it. I prefer pure Spring approach if I need to mock Spring bean in integration test.

Spring provides @Primary  annotation for specifying which bean should be preferred in the case when two beans with same type are registered. This is handy because you can override production bean with fake bean in integration test. Let’s explore this approach and some pitfalls on examples.

I chose this simplistic / dummy production code structure for demonstration:

@Repository
public class AddressDao {
	public String readAddress(String userName) {
		return "3 Dark Corner";
	}
}

@Service
public class AddressService {
    private AddressDao addressDao;
    
    @Autowired
    public AddressService(AddressDao addressDao) {
        this.addressDao = addressDao;
    }
    
    public String getAddressForUser(String userName){
        return addressDao.readAddress(userName);
    }
}

@Service
public class UserService {
    private AddressService addressService;

    @Autowired
    public UserService(AddressService addressService) {
        this.addressService = addressService;
    }
    
    public String getUserDetails(String userName){
        String address = addressService.getAddressForUser(userName);
        return String.format("User %s, %s", userName, address);
    }
}

AddressDao singleton bean instance is injected into AddressService. AddressService is similarly used in UserService.

I have to warn you at this stage. My approach is slightly invasive to production code. To be able to fake existing production beans, we have to register fake beans in integration test. But these fake beans are usually in the same package sub-tree as production beans (assuming you are using standard Maven files structure: “src/main/java” and “src/test/java”). So when they are in the same package sub-tree, they would be scanned during integration tests. But we don’t want to use all bean fakes in all integration tests. Fakes could break unrelated integration tests. So we need to have mechanism, how to tell the test to use only certain fake beans. This is done by excluding fake beans from component scanning completely. Integration test explicitly define which fake/s are being used (will show this later). Now let’s take a look at mechanism of excluding fake beans from component scanning. We define our own marker annotation:

public @interface BeanMock {
}

And exclude @BeanMock annotation from  component scanning in main Spring configuration.

@Configuration
@ComponentScan(excludeFilters = @Filter(BeanMock.class))
@EnableAutoConfiguration
public class Application {
}

Root package of component scan is current package of Application class. So all above production beans needs to be in same package or sub-package. We are now need to create integration test for UserService. Let’s spy on address service bean. Of course such testing doesn’t  make practical sense with this production code, but this is just example. So here is our spying bean:

@Configuration
@BeanMock
public class AddressServiceSpy {
	@Bean
	@Primary
	public AddressService registerAddressServiceSpy(AddressService addressService) {
		return spy(addressService);
	}
}

Production AddressService bean is autowired from production context, wrapped into Mockito‘s spy and registered as primary bean for AddressService type. @Primary annotation makes sure that our fake bean will be used in integration test instead of production bean. @BeanMock annotation ensures that this bean can’t be scanned by Application component scanning. Let’s take a look at the integration test now:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { Application.class, AddressServiceSpy.class })
public class UserServiceITest {
    @Autowired
    private UserService userService;

    @Autowired
    private AddressService addressService;

    @Test
    public void testGetUserDetails() {
        // GIVEN - spring context defined by Application class

        // WHEN
        String actualUserDetails = userService.getUserDetails("john");

        // THEN
        Assert.assertEquals("User john, 3 Dark Corner", actualUserDetails);
        verify(addressService, times(1)).getAddressForUser("john");
    }
}

@SpringApplicationConfigration annotation has two parameters. First (Application.class) declares Spring configuration under test. Second parameter (AddressServiceSpy.class) specifies fake bean that will be loaded for our testing into Spring IoC container. It’s obvious that we can use as many bean fakes as needed, but you don’t want to have many bean fakes. This approach should be used rarely and if you observe yourself using such mocking often, you are probably having serious problem with tight coupling in your application or within your development team in general. TDD methodology should help you target this problem. Bear in mind: “Less mocking is always better!”. So consider production design changes that allow for lower usage of mocks. This applies also for unit testing.

Within integration test we can autowire  this spy bean and use it for various verifications. In this case we verified if testing method userService.getUserDetails called method addressService.getAddressForUser with parameter “john”.

I have one more example. In this case we wouldn’t spy on production bean. We will mock it:

@Configuration
@BeanMock
public class AddressDaoMock {
	@Bean
	@Primary
	public AddressDao registerAddressDaoMock() {
		return mock(AddressDao.class);
	}
}

Again we override production bean, but this time we replace it with Mockito’s mock. We can than record behavior for mock in our integration test:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { Application.class, AddressDaoMock.class })
public class AddressServiceITest {
	@Autowired
	private AddressService addressService;

	@Autowired
	private AddressDao addressDao;

	@Test
	public void testGetAddressForUser() {
		// GIVEN
		when(addressDao.readAddress("john")).thenReturn("5 Bright Corner");

		// WHEN
		String actualAddress = addressService.getAddressForUser("john");

		// THEN
		Assert.assertEquals("5 Bright Corner", actualAddress);
	}

	@After
	public void resetMock() {
		reset(addressDao);
	}
}

We load mocked bean via @SpringApplicationConfiguration‘s parameter. In test method, we stub addressDao.readAddress method to return “5 Bright Corner” string when “john” is passed to it as parameter.

But bear in mind that recorded behavior can be carried to different integration test via Spring context. We don’t want tests affecting each other. So you can avoid future problems in your test suite by reseting mocks after test. This is done in method resetMock.

Source code is on Github.

Black Box Testing of Spring Boot Microservice is so easy

When I needed to do prototyping, proof of concept or play with some new technology in free time, starting new project was always a little annoying barrier with Maven. Have to say that setting up Maven project is not hard and you can use Maven Archetypes. But Archetypes are often out of date. Who wants to play with old technologies? So I always end up wiring in dependencies I wanted to play with. Not very productive spent time.

But than Spring Boot came to my way. I fell in love. In last few months I created at least 50 small playground projects, prototypes with Spring Boot. Also incorporated it at work. It’s just perfect for prototyping, learning, microservices, web, batch, enterprise, message flow or command line applications. You have to be dinosaur or be blind not to evaluate Spring Boot for your next Spring project. And when you finish evaluate it, you will go for it. I promise.

I feel a need to highlight how easy is Black Box Testing of Spring Boot microservice. Black Box Testing refers to testing without any poking with application artifact. Such testing can be called also integration testing. You can also perform performance or stress testing way I am going to demonstrate.

Spring Boot Microservice is usually web application with embedded Tomcat. So it is executed as JAR from command line. There is possibility to convert Spring Boot project into WAR artifact, that can be hosted on shared Servlet container. But we don’t want that now. It’s better when microservice has its own little embedded container.

I used existing Spring’s REST service guide as testing target. Focus is mostly on testing project, so it is handy to use this “Hello World” REST application as example. I expect these two common tools are set up and installed on your machine:

So we’ll need to download source code and install JAR artifact into our local repository. I am going to use command line to download and install the microservice. Let’s go to some directory where we download source code. Use these commands:

git clone git@github.com:spring-guides/gs-rest-service.git
cd gs-rest-service/complete
mvn clean install

If everything went OK, Spring Boot microservice JAR artifact is now installed in our local Maven repository. In serious Java development, it would be rather installed into shared repository (e.g. Artifactory, Nexus,… ). When our microservice is installed, we can focus on testing project. It is also Maven and Spring Boot based.

Black box testing will be achieved by downloading the artifact from Maven repository (doesn’t matter if it is local or remote). Maven-dependency-plugin can help us this way:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <id>copy-dependencies</id>
            <phase>compile</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <includeArtifactIds>gs-rest-service</includeArtifactIds>
                <stripVersion>true</stripVersion>
            </configuration>
        </execution>
    </executions>
</plugin>

It downloads microservice artifact into target/dependency directory by default. As you can see, it’s hooked to compile phase of Maven lifecycle, so that downloaded artifact is available during test phase. Artifact version is stripped from version information. We use latest version. It makes usage of JAR artifact easier during testing.

Readers skilled with Maven may notice missing plugin version. Spring Boot driven project is inherited from parent Maven project called spring-boot-starter-parent. It contains versions of main Maven plugins. This is one of the Spring Boot’s opinionated aspects. I like it, because it provides stable dependencies matrix. You can change the version if you need.

When we have artifact in our file system, we can start testing. We need to be able to execute JAR file from command line. I used standard Java ProcessBuilder this way:

public class ProcessExecutor {
	public Process execute(String jarName) throws IOException {
		Process p = null;
		ProcessBuilder pb = new ProcessBuilder("java", "-jar", jarName);
		pb.directory(new File("target/dependency"));
		File log = new File("log");
		pb.redirectErrorStream(true);
		pb.redirectOutput(Redirect.appendTo(log));
		p = pb.start();
		return p;
	}
}

This class executes given process JAR based on given file name. Location is hard-coded to  target/dependency directory, where maven-dependency-plugin located our artifact. Standard and error outputs are redirected to file. Next class needed for testing is DTO (Data  transfer object). It is simple POJO that will be used for deserialization from JSON. I use Lombok project to reduce boilerplate code needed for getters, setters, hashCode and equals.

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Greeting {
    private long id;
    private String content;
}

Test itself looks like this:

public class BlackBoxTest {
	private static final String RESOURCE_URL = "https://localhost:8080/greeting";

	@Test
	public void contextLoads() throws InterruptedException, IOException {
		Process process = null;
		Greeting actualGreeting = null;
		try {
			process = new ProcessExecutor().execute("gs-rest-service.jar");

			RestTemplate restTemplate = new RestTemplate();
			waitForStart(restTemplate);

			actualGreeting = restTemplate.getForObject(RESOURCE_URL, Greeting.class);
		} finally {
			process.destroyForcibly();
		}
		Assert.assertEquals(new Greeting(2L, "Hello, World!"), actualGreeting);
	}

	private void waitForStart(RestTemplate restTemplate) {
		while (true) {
			try {
				Thread.sleep(500);
				restTemplate.getForObject(RESOURCE_URL, String.class);
				return;
			} catch (Throwable throwable) {
				// ignoring errors
			}
		}
	}
}

It executes Spring Boot microservice process first and wait unit it starts. To verify if microservice is started, it sends HTTP request to URL where it’s expected. The service is ready for testing after first successful response. Microservice should send simple greeting JSON response for HTTP GET request. Deserialization from JSON into our Greeting DTO is verified at the end of the test.

Source code is shared on Github.