Unit Testing

15
Jan

Using assumptions in JUnit

Posted by eugene as Unit Testing

Besides theories, in JUnit 4.4 appeared such a feature as Assumption. The assumptions allow to specify the conditions due to which the test-case must be performed. If these conditions of test immplementation are not complied, the test is automatically considered to be successful.

Let’s suppose there’s a set of tests which include tests for implementing of which the existence of some environment is necessary – for example, you need to test EJB components that can only work in the EJB container and we want to run the test from our favourite development environment and we do not want these tests would have failed due to some timeout. Ideally these tests should be marked as missing.

Below is represented a test-case without using of assumptions. All methods marked by the @Test annotation will be implemented each time when running the test and the testEJB method will take significant time. You can mark this method by the @Ignore annotation but then you had to remove it each time the test will be run the test environment.

public class TestWithoutAssumption
{
    @Test
    public void testEJB()
    {
        // call to EJB goes here
    } 
 
    @Test
    public void testA()
    {
        // test class A
    } 
 
    @Test
    public void testB()
    {
        // test class B
    }
}

Let’s re-run this test with using of assumptions:

public class TestWithAssumption
{
    public boolean isTestEnvironment()
    {
        // logic to test environment detection
    } 
 
    @Test
    public void testEJB()
    {
        assumeTrue(isTestEnvironment())
        // call to EJB goes here
    } 
 
    @Test
    public void testA()
    {
        // test class A
    } 
 
    @Test
    public void testB()
    {
        // test class B
    }
}

As such the testEJB test will be only run in case if it’s launched in test environment. The rest tests will be run every time.

13
Jan

Using theories in JUnit

Posted by eugene as Unit Testing

In time of refactoring of unit tests I’ve met a very intereting and useful feature of JUnit (starting from version 4.4): Theory.

Usually when you tet functionality a big amount of tests is written which check the work of code depending on the different sets of input data. However, the code of the test doesn’t change itself. All this degenerates into the test case consisting of a dozen of identical methods.

Theories allow you to specify a set of data on which the test should be performed.

Below is shown the bulk of code without using of theories:

public class TestWithoutTheory
{
     @Test
	 public void testStringA()
     {
         String s = "A";
         callSomeBusinessLogic(s);
 
		 // asserts go here
     }
 
     @Test
	 public void testStringB()
     {
         String s = "B";
         callSomeBusinessLogic(s);
 
		 // asserts go here
	 }
 
	 @Test
	 public void testStringC()
     {
         String s = "C";
         callSomeBusinessLogic(s);
 
		 // asserts go here
     }
}

As you can see the methods differ only with the set of data which are transferred into the callSomeBusinessLogic method.

It would be wrong to combine all this into one single method as if an error occurs when you use one of the data sets, the entire test will fail which is wrong.

The Theory will help to resolve this problem. Below is represented the test re-written with the help of the theory:

@RunWith(Theories.class)
public class TestWithTheory
{
     public static @DataPoints String[] values = {"A",
"B", "C"};
 
      @Theory
	  public void testString(String s)
	  {
		callSomeBusinessLogic(s);
 
		// asserts go here
	}
}

A more compact test will appear in the result which is easier to maintain by the way as the changes should be made only for one method and if it’s necessary to add a new set ther’s no need to add a new method.

28
Dec

Integration tests with Maven, JUnit and Spring

Posted by eugene as Unit Testing

Unit tests is a good thing but my point is that it’s wrong to rely just on them. Sometimes it’s very important to check how several classes work and sometimes how do application leyers work together.

Our application actively uses Spring and it means the integration tests also should work with the Spring context.

Often these tests can be time consuming, even if you use the in-memory database (by the way I’ve checked why – we have too many JPA entities with multiple properties in each one and the Hibernate initialization, exactly it takes dozens of seconds!)

So we need to make sure that to do so that these tests wouldn’t have been implemented by default unlike conventional unit tests and wouldn’t have been implemented during the build.

The plan is as follows:

  • 1. Write the services divided into two application layers
  • 2. Join them by Spring
  • 3. Write the test by using JUnit for interaction of these services (real ones without any mocks)
  • 4. Make sure this test is not run by default during the build
  • 5. Profit!

1. Writing services divided into two application layers

Why two layers? I prefer to divide the business logic into the layers (for example, the persistency layer) in order the code won’t turn into spaghetti. Layers have hierarchy – which one can call and by dividing the layers into the different maven projects I’m checking that there are no forbidden relationships.

Hence, let’s create pom.xml of the whole application with the dependencies which are necessary for all:

<?xml version="1.0" encoding="UTF-8"?>
<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.mycompany</groupId>
    <artifactId>myapp</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
 
    <name>myapp</name>
 
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring-version>3.1.0.RELEASE</spring-version>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.8.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring-version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring-version}</version>
        </dependency>
    </dependencies>
 
    <modules>
        <module>backend</module>
        <module>frontend</module>
        <module>gui</module>
    </modules>
 
</project>

It contains three modules – gui, frontend and backend (two layers of business logics).

Here is pom.xml for frontend:

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.mycompany</groupId>
    <artifactId>myapp</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>
  <groupId>com.mycompany</groupId>
  <artifactId>frontend</artifactId>
  <version>1.0-SNAPSHOT</version>
  <name>frontend</name>
    <dependencies>
        <dependency>
            <groupId>com.mycompany</groupId>
            <artifactId>backend</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

As you can see it depends on backend and all the rest receives from the parent pom.

Here is pom.xml for backend:

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
         xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.mycompany</groupId>
        <artifactId>myapp</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <groupId>com.mycompany</groupId>
    <artifactId>backend</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>backend</name>
</project>

Let’s create the following in the backend/src/main/java/com/mycompany/service/backend directory:

IBackendService1.java:
package com.mycompany.service.backend;
 
public interface IBackendService1 {
    String computeSecretString();
}
BackendService2.java:
 
package com.mycompany.service.backend;
 
public interface IBackendService2 {
    int computeSecretNumber();
}
BackendService1.java:
 
package com.mycompany.service.backend;
 
public class BackendService1 implements IBackendService1 {
    @Override
    public String computeSecretString() {
        return "James Bond";
    }
}
BackendService2.java:
 
package com.mycompany.service.backend;
 
public class BackendService2 implements IBackendService2 {
    @Override
    public int computeSecretNumber() {
        return 7;
    }
}

Let’s create the following in the frontend/src/main/java/com/mycompany/service/frontend directory:

IFrontendService.java:

package com.mycompany.service.frontend;
 
 
public interface IFrontendService {
    String getAgent();
}

FrontendService.java:

package com.mycompany.service.frontend;
 
 
import com.mycompany.service.backend.*;
 
public class FrontendService implements IFrontendService {
 
    private IBackendService1 backendService1;
    private IBackendService2 backendService2;
 
    public FrontendService(IBackendService1 backendService1, IBackendService2 backendService2) {
        this.backendService1 = backendService1;
        this.backendService2 = backendService2;
    }
 
    @Override
    public String getAgent() {
        return backendService1.computeSecretString()+backendService2.computeSecretNumber();
    }
}

2. Joining them by Spring

backend/src/main/resources/backend-beans.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
 
    <bean id="names_provider" class="com.mycompany.service.backend.BackendService1"/>
    <bean id="secret_service" class="com.mycompany.service.backend.BackendService2"/>
 
</beans>

frontend/src/main/resources/frontend-beans.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
       ">
    <import resource="classpath:backend-beans.xml"/>
 
    <bean id="agent_service" class="com.mycompany.service.frontend.FrontendService">
        <constructor-arg index="0" ref="names_provider"/>
        <constructor-arg index="1" ref="secret_service"/>
    </bean>
 
</beans>

3. Let’s write the test by using JUnit for interaction of these services (real ones without any mocks)

In the frontend/src/test/java/com/mycompany/integration directory

FrontendServiceTest.java:

package com.mycompany.integration;
 
 
import com.mycompany.service.frontend.IFrontendService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
import static org.junit.Assert.assertTrue;
 
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
@ContextConfiguration(locations = {"classpath:frontend-beans.xml"})
public class FrontendServiceTest {
 
    @Autowired
    IFrontendService frontendService;
 
    @Test
    public  void testBond() {
        String agent = frontendService.getAgent();
        assertTrue("It should be Bond", agent.contains("Bond"));
    }
 
}

Pay attention on the annotations:

@RunWith(SpringJUnit4ClassRunner.classMode.AFTER_EACH_TEST_METHOD) – runs tests with the Spring “launcher”

@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) – will create Spring context from scratch for each test method

@ContextConfiguration(locations = {“classpath:frontend-beans.xml”}) – where to take beans

there’s also @ActiveProfiles(profiles = “local”) – if you don’t use profiles.

@AutoWired will inject the beans into the test class. If you have more than one bean realizing the definite interface you can replace it on @Resource(name = “session-operations”)

4. Let’s do so that this test won’t run by default during the build

Let’s add the following into pom.xml of the whole application:

 <properties>
        ...
        <systest.package>**/com/mycompany/integration/**</systest.package>
    </properties>
 
 <build>
       ....    
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.5</version>
                <configuration>
                    <excludes>
                        <exclude>${systest.package}</exclude>
                        <exclude>**/*$*</exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

Now the test won’t launch during the build. (We’ve also mentioned the inner classes are not tests).

And to have an opportunity to run it not only by name, let’s add the profile where there’s no such exclude:

<profiles>
        <profile>
            <id>systest</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-surefire-plugin</artifactId>
                        <version>2.5</version>
                        <configuration>
                            <excludes>
                                <exclude>**/*$*</exclude>
                            </excludes>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>

Now all the tests located in com.mycompany.integration won’t run during the build and to launch them it’s necessary to install the “systest” maven profile, for example as follows:

mvn -P=systest test

5. Profit!