Wednesday, December 16, 2015

Use Spring Test without @RunWith(SpringJUnit4ClassRunner.class)

This is a new feature from Spring framework 4.2. Now you can use other JUnit's runners,like Parameterized or MockitoJUnitRunner but without losing spring test benefits. (with all the features you love with spring-test like spring  Dependency Injection , Auto-rollback Transaction for test and etc).

In this article, a simple hello world level JUnit test case is provided with JUnit Parameterized runner, with spring-test support enabled.

0. What you need

  • JDK 1.7 +
  • Spring framework 4.2 + ( 4.2.1.RELEASE is used in this demo)
  • Maven 3.2+ (This demo is a maven project, but maven is not necessary for enable Spring-test support in other JUnit runners)

1. Define 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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.shengwang.demo</groupId>
  <artifactId>spring-test-simple</artifactId>
  <version>1</version>
  <packaging>jar</packaging>

  <name>spring-test-simple</name>
  <url>http://maven.apache.org</url>

  <dependencies>
    <!-- Spring framework -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>4.2.1.RELEASE</version>
    </dependency>

    <!-- Spring test -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>4.2.1.RELEASE</version>
      <scope>test</scope>
    </dependency>
    
    <!-- JUnit test -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

  </dependencies>
  
  <build>
    <plugins>
      <!-- Use Java 1.7 -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.5.1</version>
        <configuration>
          <source>1.7</source>
          <target>1.7</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

The pom specifies 3 dependencies, spring-context, spring-test and junit. Also it specify the Java version to 1.7.

2. Define Java Class

There are 3 classes in this demo. First is HelloService.java, which is a Spring bean as test target. 

package com.shengwang.demo;

import org.springframework.stereotype.Service;

@Service
public class HelloService {

  public String sayHello(String name) {
    return "Hello " + name;
  }
}

The second is JavaConfig.java, as Spring context configuration.

package com.shengwang.demo;

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

@Configuration
@ComponentScan
public class JavaConfig {}

The Last is the JUnit test case HelloServiceTest.java use Parameterized as runner.

package com.shengwang.demo;

import java.util.Arrays;
import java.util.Collection;

import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.rules.SpringClassRule;
import org.springframework.test.context.junit4.rules.SpringMethodRule;

@RunWith(Parameterized.class)
@ContextConfiguration(classes=JavaConfig.class)  // specify context config
public class HelloServiceTest {
  
  // -------------------------------------------
  //  spring test support requirement from 4.2
  // -------------------------------------------
  @ClassRule
  public static final SpringClassRule SPRING_CLASS_RULE= new SpringClassRule();
  @Rule
  public final SpringMethodRule  springMethodRule = new SpringMethodRule();
  // -------------------------------------------
  //  spring test support requirement over
  // -------------------------------------------
  
  private String name;
  
  @Autowired
  HelloService service;

  public HelloServiceTest(String name) {
    this.name = name;
  }
  
  @Parameters
  public static Collection<String[]> data() {
    return Arrays.asList(new String[][] {
        {"Tom"},{"Jerry"}  
    });
  }
  
  @Test
  public void testSayHello() {
    service.sayHello(name);
  }
  
}

The test case enable spring-test support by 3 steps:

  1. 1. Use @ContextConfiguration to config Spring TestContext .
  2. 2. Add a SpringClassRule static variable
  3. 3. Add a SpringMethodRule field variable

The project's hierarchy looks like below:

image_thumb5

Now the test get all abilities from spring-test. The @Autowired dependency injection works perfectly.

image_thumb2

3 comments:

  1. actually this is just one post with valid information, that is really working... Thank you Sheng!

    ReplyDelete
  2. Thanks a lot. This is working.

    ReplyDelete
  3. java.lang.IllegalStateException: Failed to find 'public SpringMethodRule' field in test class

    ReplyDelete

Powered by Blogger.

About The Author

My Photo

Has been a senior software developer, project manager for 10+ years. Dedicate himself to Alcatel-Lucent and China Telecom for delivering software solutions.

Pages

Unordered List