Showing posts with label Test. Show all posts
Showing posts with label 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!

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