Showing posts with label Spring Test. Show all posts
Showing posts with label Spring Test. Show all posts

Wednesday, March 2, 2016

How to separate integration test from unit test

0. Environment

  • Maven  3.2 +
  • Eclipse
  • JUnit 4.+

This article supposes you already know how to use maven plugin failsafe for integration test.

1. Final goal

Let's clarify the word "separate" first. It means:

  • Unit tests and integration tests are in different directories, but stay in the same maven project (not in another maven module project).

Also we want achieve these goals:

  • In Eclipse, you can choose to only run unit tests alone,  without integration tests
  • In Eclipse, you can choose to only run integration tests alone,  without unit tests
  • In maven command line, you can choose to only run unit tests alone,  without integration tests (mvn test)
  • In maven command line, you can choose to only run integration tests alone,  without unit tests (mvn clean test-compile failsafe:integration-test)
  • In maven command line, you can also run them both with a single command. (mvn verify)

The final directory hierrarchy looks like below.

image

The project hierrarchy in Eclipse looks like below.

image

Just remember Eclipse and Maven have different ways to manage build process, so both Eclipse and Maven pom file need to be configured respectively.

2. Create directories for integration test

Create 2 directories in your project

  • src/integration/java
  • src/integration/resources

3. Configure in Eclipse

3.1 add source folder

In Eclipse, right click you project's name -> new -> Source Folder.  ( Not folder, but Source Folder)

image

The click button  "Brower" for Folder name.

 image

Select /src/integration/java, then OK, to add this directory to Eclipse build path.

image

Now you should see a new source folder added to you project in Eclipse.

image

Repeat to add another source folder /src/integration/resources. Now you project in Eclipse should looks like below.

image

3.2 Config Eclipse build path

Since integration tests are also tests, you don't want them mixed with main code. So you need to change the "Build Path" in Eclipse. By default, newly added source folders are treated as main code/resources.

Right click project name -> Build Path -> Configure Build Path

image

Change Output folder for both /src/integration/java and /src/integration/resources to target/test-classes

image

Up to now, you should be about to run unit test and integration tests separately, by choose different dir to run.

4. Configure pom.xml

By default, maven can only have 1 test source directory, which point to ${project.basedir}/src/test/java. To add our newly created dir to maven as test source/resource, we need to utilize plugin 'build-helper'.

4.1 config failsafe plugin

Add failsafe plugin in <build>

  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-failsafe-plugin</artifactId>
    <version>2.19.1</version>
    <configuration>
      <encoding>UTF-8</encoding>
    </configuration>
    <executions> 
      <execution> 
        <id>integration-test</id> 
        <goals> 
          <goal>integration-test</goal>
          <goal>verify</goal> 
        </goals> 
      </execution> 
    </executions> 
  </plugin>

4.2 config build-helper plugin

Add build-helper plugin to <build>.

  <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>build-helper-maven-plugin</artifactId>
    <version>1.9.1</version>
    <executions>
      <execution>
        <id>add-test-source</id>
        <goals>
          <goal>add-test-source</goal>
        </goals>
        <configuration>
          <sources>
            <source>${project.basedir}\src\integration\java</source>
          </sources>
        </configuration>
      </execution>
      <execution>
        <id>add-test-resource</id>
        <goals>
          <goal>add-test-resource</goal>
        </goals>
        <configuration>
          <resources>
            <resource>
              <!-- Don't forget <directory> label -->
              <directory>${project.basedir}\src\integration\resources</directory>
            </resource>
          </resources>
        </configuration>
      </execution>
    </executions>
  </plugin>

Now maven should also be happy with separated integration tests. Run command

mvn test

The unit test cases are invoked.

image

You can also just run integration test cases without packaging  or running unit tests . (For example when you are writing integration test cases).

mvn clean test-compile failsafe:integration-test

You can also run both unit test cases and integration test cases with command.

mvn verify

4.3 Errors may occure in Eclipse

After adding plugin 'build-helper', Eclipse may report errors complain about "execution not covered", just following the autofix from Eclipse.

image

This will add Eclipse's maven lifecycle mapping configuration, which stored in file <Eclipse workspace>/.metadata/.plugins/org.eclipse.m2e.core/lifecycle-mapping-metadata.xml

You can empty this file to undo your change later.

Happy testing!

Friday, February 26, 2016

Play with spring integration test

This article is mainly about using spring-test for integration test. Some basic issuses of integration test are discussed for projects with following natures:

  • It's a maven project
  • It's also a Spring webmvc proejct
  • It's developed in Eclipse.

This article is based on Spring framework 4.2 +

0. Basics

0.1  Unit test and  integration test

Test target of unit test is every individual POJO, if this target object need other external objects, mock them. In a spring web/rest application, unit tests run without Spring context.  JUnit test runner usually sets to someone from mock framework.  such as @RunWith(MockitoJUnitRunner.class) if Mockito is used.

From "unit test"'s view, it doesn't matter if you application is a Spring mvc application. It even doesn't matter if you application uses spring or not. Because unit tests only verify a bunch of  classes one by one, decouple them with the help of mock.

Test target of integration test is functions of a group of components. In a spring web/rest application, spring-test provide Spring TestContext Frame to simulate a servlet container, so your code can run in a web context without  deployment to a real server. JUnit test runner usually sets to the one provided by spring-test, like @RunWith(SpringJUnit4ClassRunner.class) 

0.2 JUnit, Running test in eclipse, Running test by maven

JUnit is just a test framework, it has no idea of the currently running test is a "unit test" or an "integration test".  Or we can say, a JUnit test case can be a unit test, it can also be a integration test.

Eclipse has no concept of integration test but only unit test. Eclipse has no requirement about test case class names.  e.g a class named "BookRestControllerAbc.java" can run as a JUnit test.

Maven uses plugin "surefire" to run "unit test", use plugin "failsafe" to run "integration" test.  They have decidedly naming conventions.

Surefire plugin by default will automatically run test cases with following names:

  • "**/Test*.java"
  • "**/*Test.java"
  • "**/*TestCase.java"

Failsafe plugin by default will automatically run test cases with following names:

  • "**/IT*.java"
  • "**/*IT.java"
  • "**/*ITCase.java"

So the test cases can be categoried by their class names.

0.3 Watch out dir /src/main/webapp

Even it's a maven project in eclipse, eclipse and maven have their own way to manage dependencies and resources. This is the key to understand the problems like "Why I can run my test case in eclipse but will can't run the same test in maven build?" or the reverse one,  "Why  I can my test case with maven but can't run it in Eclipse". Usually the cause of these kinds of problems seem like can't find some resources in a certain senario.

Here is the dilemma:

In Eclipse, create a maven web application will create directory /src/main/webapp as web root.

web.xml must in directory /src/main/webapp/WEB-INF. The default spring configuration, either applicationContext.xml or dispatcher-servlet.xml  required by Spring webmvc need to be in the same directory /src/main/webapp/WEB-INF.

But by default /src/main/webapp is NOT a resource directory for both maven and Eclipse.

You can succeed in "Run on Server" in Eclipse, because web root /src/main/webapp also get packaged with the help of package behavior for war in Eclipse.

But for integration tests, they will definitly fail no matter using maven or in Eclipse.   Since neither applicationContext.xml nor dispatcher-servlet.xml  in web/main/webapp are recognized as resources. The error message will be some kind of file not found.

If you simply move, e.g dispatcher-servlet.xml from /src/main/webapp/WEB-INF/ to maven's resource path like /src/main/resources/WEB-INF/,  the application can no longer run in Eclipse.  The error message will complain can not find /WEB-INF/dispatcher-servlet.xml or similar.

So here's the best way in my experience:

  1. Move spring configuration applicationContext.xml or dispatcher-servlet.xml to maven resources path /main/resources
  2. In web.xml, set context location for servlet dispatcher like below
  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
 
    <!--  specify location of spring config, don't use default -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:dispatcher-servlet.xml</param-value>
    </init-param>
    
    <load-on-startup>1</load-on-startup>
  </servlet>

The final project hierarchy like below.

image

 

Since resources are now in the path recognized by both Maven and Eclipse, we can successfully run our web application in Eclipse as well as run integration test in maven command line or in Eclipse.

This probably make you stop using xml-styled spring configuration and embrace the Java-styled spring configuration for the sake of a easier life.

1. Config pom.xml for integration test

Maven has failsafe plugin for integration test.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-failsafe-plugin</artifactId>
  <version>2.19.1</version>
  <configuration>
    <encoding>UTF-8</encoding>
  </configuration>
  <!-- uncomment to bind maven lifecycle
  <executions> 
    <execution> 
      <id>integration-test</id> 
      <goals> 
        <goal>integration-test</goal>
        <goal>verify</goal> 
      </goals> 
    </execution> 
  </executions> 
  -->
</plugin>

Without bind to any lifecycle like above, you can run integration test with command

mvn failsafe:integration-test

2. Spring integration test case with JUnit

Cheat sheet for creating Spring integration test with JUnit.

  1. Set JUnit runner @RunWith(SpringJUnit4ClassRunner.class)
  2. Load spring config with @WebAppConfiguration and @ContextConfiguration("classpath:dispatcher-servlet.xml")
  3. @Autowire WebApplicationContext wac in JUnit test case class.
  4. Create MockMvc in JUnit's @Before method, mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
  5. In test case method, use MockMvc.perform(MockMvcRequestBuilders.xxx()) to send request and andExpect(MockMvcResultMatchers.yyy) to verify response.

3. possible issues

3.1 javax.servlet.ServletContext  Not Found

This error message often happens when running integration test with plugin failsafe. You may see an error message like below.

Failed to instantiate [org.springframework.test.context.web.WebDelegatingSmartContextLoader]: Constructor threw exception; nested exception is java.lang.NoClassDefFoundError: javax/servlet/ServletContext

Add the following dependency can solve the problem.

  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.0.1</version>
    <scope>provided</scope>
  </dependency>

Make sure the major version (3.0) should be same as specified in web.xml.

3.2 Could not obtain DataSource when using @Sql + JPA

When testing, you probably like to use memory database such as HSQL, H2 or Derby.  If all the connection configurations. e.g driver, url, username and password, are in JPA /META-INF/persistence.xml file, not in spring's configuration, you may see an error like below complain couldn't obtain DataSource from transaction manager when using @Sql to insert test data before a certain test case.

java.lang.IllegalStateException: Failed to execute SQL scripts for test context ..(some text ignored).. could not obtain DataSource from transaction manager [org.springframework.orm.jpa.JpaTransactionManager] (named '').

This because the @Sql need DataSource in spring context, just add a DataSource in your spring configuration. Make sure the this datasource point to the same database as  in the persistence.xml. For example, if the persistence.xml using a H2 database like below,

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
 xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
 <persistence-unit name="spring-persistence-jpa-tx" transaction-type="RESOURCE_LOCAL">
  <provider>org.hibernate.ejb.HibernatePersistence</provider>
  <exclude-unlisted-classes>false</exclude-unlisted-classes>
  <properties>
  
   <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
   <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:mydb" />
   <property name="javax.persistence.jdbc.user" value="sa" />
   <property name="javax.persistence.jdbc.password" value="" />
   
   <!-- Automatically drop then create table -->
   <property name="hibernate.hbm2ddl.auto" value="create" />
   <!-- print out sql  -->
   <property name="hibernate.show_sql" value="true"/>
  </properties>
 </persistence-unit>
</persistence>

Then we need to add a DataSource bean in Spring configure to be able to use @Sql annotation. In xml-styled configuration, with spring-jdbc's help, it's easy to create a datasource using embedded database.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
  xmlns:jdbc="http://www.springframework.org/schema/jdbc"
  xsi:schemaLocation="
    http://www.springframework.org/schema/jdbc
    http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">

  <!--  omit other beans  -->
  
  <jdbc:embedded-database id="usually_is_dataSource" type="H2" database-name="mydb">
    <!-- 
    <jdbc:script location="classpath:anyfile1.sql"/>
    <jdbc:script location="classpath:anyfile2.sql"/>
     -->
  </jdbc:embedded-database>
</beans>

Notice if using the embedded memory, the database'name is important. The database-name MUST be same as what in persistence.xml. In  this demo it's 'mydb'.

4. A complete demo

In the end of the article,a demo of integration test for get resource of a REST web service is provided. It inerts test data before test running, then runs the junit test case try to access a url and check the status of the response is 200. Finally, restores the database by deleting the inserted test data.

package com.shengwang.demo.controller;

// import ignored

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration({"classpath:/WEB-INF/dispatcher-servlet.xml"})
public class BookRestControllerIT {
  @Autowired
  WebApplicationContext wac;
  private MockMvc mockMvc;

  @Before
  public void setUp() throws Exception {
    mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
  }

  @Test
//  @Sql(scripts="classpath:insertOneBook.sql")
  @SqlGroup ({
      @Sql(statements="insert into book (book_id,title) values (1,'Test title')"),
      @Sql(statements="delete from book where book_id=1",executionPhase=ExecutionPhase.AFTER_TEST_METHOD)
  })
  public void testFindBookWithExistBookId() {
    try {
      mockMvc.perform(get("/book/1")).andExpect(MockMvcResultMatchers.status().isOk());
    } catch (Exception e) {
      fail("testFindBookWithExistBookId failed");
    }
  }
}

It passes successfully in Eclipse.

image

It also passes in maven command line invocation. 

mvn clean test-compile failsafe:integration-test

5. More

Here provides a hello world level spring integration test case. In read usage, you should separate your unit test cases from integration test cases. That need more configuration in Eclipse (in Java Build Path) and in Maven pom.xml (with the help of plugin build-helper-maven-plugin) respectively. See another tutorial on "How to separate integration test from unit test"

Tuesday, February 16, 2016

How to use H2 embeded database in spring application

H2, as a embeded memory database, is mainly used for development and test phase. H2 also has a web console which is very convenient.

image

1. Use H2 in Spring boot application

Spring boot has almost everything done for you. You need to do 2 steps, first inluce H2 in you pom.xml.

  <!-- H2 -->
  <dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
   <scope>runtime</scope>
  </dependency>

The version of H2 is managed by spring boot and  scope can be set to runtime because you code should not have any APIs come from H2 itself. Then specify datasource url to use H2 in the application.properties file or in lieu of yaml styled configuration

# define DataSrouce properties 
# use h2 can have a buid in web console http://localhost:8080/h2-console
spring.datasource.url=jdbc:h2:mem:mydb

In spring boot application, there is no need to set username and password for using H2 memory database, because H2's default username is 'sa' (or SA, case insensitive), default password is empty. These default username/password will be auto configured by spring boot.  Also Spring boot will config the H2 web console for you automatcally at http://localhost:8080/h2-console

Use H2 in spring-mvc application (non-spring boot)

In non-spring boot application, which is normall just powered by spring-webmvc and other spring artifacts, using H2 need 3 steps. First is also to include H2 in pom.xml

  <!-- H2 -->
  <dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
   <version>1.4.190</version>
  </dependency>

Secondly,  use H2 in any ORM configuration. For example if your project uses JPA as persistence and uses Hibernate as JPA service provider. The JPA's configuration file /META-INF/persistence.xml may looks like below.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
 xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
 <persistence-unit name="spring-persistence-jpa-tx" transaction-type="RESOURCE_LOCAL">
  <provider>org.hibernate.ejb.HibernatePersistence</provider>
  <exclude-unlisted-classes>false</exclude-unlisted-classes>
  <properties>
   <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
   <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:mydb" />
   <property name="javax.persistence.jdbc.user" value="sa" />
   <property name="javax.persistence.jdbc.password" value="" />
   
   <!-- Automatically drop then create table -->
   <property name="hibernate.hbm2ddl.auto" value="create" />
   <!-- print out sql  -->
   <property name="hibernate.show_sql" value="true"/>
  </properties>
 </persistence-unit>
</persistence>

Specify the jdbc driver, jdbc url and etc , just like other database.

Finally, and optionally, If you want the H2 web console, you need to add a bean into spring context. In xml-styled spring configuration file, it looks like below.

 <bean id="h2WebServer" class="org.h2.tools.Server" factory-method="createWebServer"
  init-method="start" destroy-method="stop">
  <constructor-arg value="-web,-webAllowOthers,-webDaemon,-webPort,8082" />
 </bean>
In java-styled spring configuration, it looks like below.
@Configuration
@ComponentScan
public class JavaConfiguration {
 //... other beans
 
 @Bean(initMethod="start",destroyMethod="stop")
 public org.h2.tools.Server h2WebConsonleServer () throws SQLException {
   return org.h2.tools.Server.createWebServer("-web","-webAllowOthers","-webDaemon","-webPort", "8082");
 }
}

The -webDaemon means run H2 web console in a daemon thread which will not block the application process termination. -webPort can set the web console service port, default is 8082. The web console url is http://localhost:8082 (Different from the web console url in spring boot application)

Use H2 in spring boot application

In a spring boot application, first add H2 to the dependency.

  <dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.182</version>
    <scope>runtime</scope>
  </dependency>

Choose a proper H2 version to use and also set scope to runtime become you should not expliity use any class from H2 in you application.

Then configure application.properties or application.yml file for spring boot datasource. Following is example in application.yml file.

spring:
  datasource:
    url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    username: sa
    password:
    driver-class-name: org.h2.Driver
    platform: h2

  # enable H2 web console and set url for web console
  # http://localhost:8080/console
  h2:
    console:
      enabled: true
      path: /console
The default username for H2 is 'sa', default password is empty.

If you also use spring security in your application, which means spring security is in your pom.xml like below.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Then you need to add a line in your spring security configuration to make H2 web console working.

@EnableWebSecurity
 
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
 
 //......
 
 
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().antMatchers("/").permitAll(); 
    http.authorizeRequests().antMatchers("/imgs/**").permitAll(); 
    http.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN");
    http.authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin();
  
    // add this line to use H2 web console
    http.headers().frameOptions().disable();
  }
}

Without this line to setup frame in spring security, you will see a empty page after you login the H2  web console.

Wednesday, January 27, 2016

Optimize static import assist in Eclipse

In order to make eclipse can auto-import Static methods, we need to make a little config in Eclipse. This article shows how to make the config in Eclipse to make auto-import for static methos works. Also this article list some classes you may need to set in Eclipse useful for your real projects.

1. How to setup content assist for static import in Eclipse

For example, although JUnit is in the classpath, by default eclipse can not give any help to import static method like assertEqual, which is a static method of class org.junit.Assert, see the screenshot below:

image

To make content assist works for static methods in Eclipse, you need to add the class which contain this static method to Eclipse, which in this demo is class org.junit.Assert.  First open menu "Windows ->  Preference".

image

image

Then, from left, find "Java -> Editor -> Content Assist -> Favorites". The word "Type" on the right may be misleading. it actually means the class that contain the static method.

image

Click Ok until finish. Now press "ctrl + 1", the content assist can give you helpful import suggestions like below:

image

2. Some useful classes for static import

Besides the org.junit.Assert in the previous example, here are more you may need in real development.

JUnit
  • org.junit.Assert
hamcrest
  • org.hamcrest.MatcherAssert
  • org.hamcrest.Matchers
Mockito
  • org.mockito.Mockito
PowerMock
  • org.powermock.reflect.Whitebox
Spring MVC test
  • org.springframework.test.web.servlet.request.MockMvcRequestBuilders
  • org.springframework.test.web.servlet.result.MockMvcResultHandlers
  • org.springframework.test.web.servlet.result.MockMvcResultMatchers
  • org.springframework.test.web.servlet.setup.MockMvcBuilders

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

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