Java Unit Testing: Mock A Spring Bean Using a Test Spring Context

Spring has a lot of ways to mock beans for testing. Here we'll be setting up a Spring context just for testing, which will have a mock bean.

First, make a new Maven project called 'mock':


mvn archetype:generate -DgroupId=org.vpxd.example -DartifactId=mock -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false


Add Spring to your project:

/pom.xml


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.vpxd.example</groupId>
  <artifactId>mock</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>mock</name>
  <url>http://maven.apache.org</url>
  <properties>
     <maven.compiler.source>1.7</maven.compiler.source>
     <maven.compiler.target>1.7</maven.compiler.target>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>4.3.3.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>4.3.3.RELEASE</version>
      <scope>test</scope>
  </dependency>
  </dependencies>
</project>


Create a main method that loads up the Spring context:

/src/main/java/org/vpxd/example/App.java


package org.vpxd.example;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class App {
  public static void main(String... args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);

    Example example = context.getBean(Example.class);

    System.out.println(example.run());
  }
}


Create a simple Spring context configuration:

/src/main/java/org/vpxd/example/Config.java


package org.vpxd.example;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages="org.vpxd")
public class Config {

}


Create a Spring bean:

/src/main/java/org/vpxd/example/Example.java


package org.vpxd.example;

public interface Example {
  String run();
}


/src/main/java/org/vpxd/example/ExampleImpl.java


package org.vpxd.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Qualifier;

@Service
public class ExampleImpl implements Example {

  private MockMe mockMe;

  @Autowired
  public ExampleImpl(MockMe mockMe) {
    this.mockMe = mockMe;
  }

  @Override
  public String run() {
    return mockMe.run();
  }
}


Create a Spring bean that will be mocked during testing

/src/main/java/org/vpxd/example/MockMe.java


package org.vpxd.example;

public interface MockMe {
  String run();
}


/src/main/java/org/vpxd/example/MockMeImpl.java


package org.vpxd.example;

import org.springframework.stereotype.Service;

@Service
public class MockMeImpl implements MockMe {

  @Override
  public String run() {
    return "real impl";
  }

}


Create a jar and then run the project, "real impl" should be output:


mvn assembly:assembly -DdescriptorId=jar-with-dependencies
java -cp target/mock-1.0-SNAPSHOT-jar-with-dependencies.jar org.vpxd.example.App


Now we're ready to add the unit test with a testing context configuration. It tests that "test impl" is returned:

/src/test/java/org/vpxd/example/ExampleTest.java


package org.vpxd.example;

import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.beans.factory.annotation.Autowired;

@RunWith(SpringRunner.class)
@ContextConfiguration(classes=ConfigWithMock.class)
public class ExampleTest {

  @Autowired
  private Example example;

  @Test
  public void testRun() {
    assertEquals("test impl", example.run());
  }
}


Now the test context configuration we used in the unit test above. Instead of component scanning everything, a filter is set up to exclude the actual implementation of the MockMe interface and the actual Config class. A mock implementation is manually defined.

/src/test/java/org/vpxd/example/ConfigWithMock.java


package org.vpxd.example;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.FilterType;

@Configuration
@ComponentScan(basePackages="org.vpxd", 
  excludeFilters = {@ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=MockMe.class),
  @ComponentScan.Filter(Configuration.class)})
public class ConfigWithMock {

  @Bean
  public MockMe getMockMe() {
    return new MockMeMockImpl();
  }
}


Finally we add the mocked implementation of the MockMe interface:

/src/test/java/org/vpxd/example/MockMeMockImpl.java


package org.vpxd.example;

import org.springframework.stereotype.Service;

@Service
public class MockMeMockImpl implements MockMe {

  @Override
  public String run() {
    return "test impl";
  }

}


Now just run the test:


mvn test


No comments:

Post a Comment