Saturday, October 19, 2013

Java Unit testing: Mocking JNDI, final classes, statics and private parts

To improve quality of an application, it is a good practice to provide unit tests together with the code which is written. This makes it easier to determine the impact of changes and fix them early during development.

Writing unit tests can be challenging. Often applications run inside an application server which provides services and a unit test runs outside of the server. Applications can depend on the services provided by the application server which makes testing outside of this scope difficult. One of those services is JNDI. JNDI makes it possible for an application to look up for example a datasource. Luckily JNDI can be provided outside of application server scope.

To make mocking methods in unit tests easier, the Mockito framework (https://code.google.com/p/mockito/) can be used. Mockito however uses a subclassing mechanism which does not work when dealing with final classes. Also static and private methods cannot be mocked because of this. PowerMock provides solutions for those issues; https://code.google.com/p/powermock/.

Implementation

First we'll create an OracleConnectionPoolDatasource which we then make available outside of application server scope via JNDI. Next there is an example how final classes, private fields/methods and statics can be mocked.

OracleConnectionPoolDatasource

We want to create an OracleConnectionPoolDatasource since we'll be working with an Oracle database. The Oracle datasource class is not present in a legal public Maven repository due to license restrictions.

You can however still use Maven for this dependency as follows;

Download the driver from Oracle;
http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-112010-090769.html

Define a dependency in your pom file as below and make sure the jar file is in the correct place.

<dependency>
    <groupId>com.oracle</groupId>
    <artifactId>ojdbc</artifactId>
    <version>14</version>
    <scope>system</scope>
    <systemPath>${basedir}/lib/ojdbc6.jar</systemPath>   <--- must match file name
</dependency>

The above is also useful when you're working with Liquibase (http://www.liquibase.org/) from Maven.

Mocking JNDI

JNDI can be mocked with; org.springframework.jndi.support.SimpleNamingContextBuilder

A Maven dependency for this;

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>3.0.6.RELEASE</version>
</dependency>

Below some sample code to create an Oracle Db testuserDS datasource and make it available via JNDI.

SimpleNamingContextBuilder builder = null;
OracleConnectionPoolDataSource ds = new OracleConnectionPoolDataSource();
ds.setURL("jdbc:oracle:thin:@localhost:1521:xe");
ds.setUser("testuser");
ds.setPassword("testuser");
builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder();
builder.bind("java:comp/env/jdbc/testuserDS", ds); // mocking for default Java application lookups
builder.bind("jdbc/testuserDS", ds); // mocking for hibernate

The above code can be placed in the BeforeClass method if you're using JUnit.

Mocking final classes, statics and private parts

Mockito in combination with Powermock make your life a lot easier to accomplish the tasks as specified in the topic. The class to be tested;

public final class App {

    private String getGreeting() {
        return "Hello";
    }
    
    public static String getName() {
        return "Maarten";
    }

    public String helloName() {
        return getGreeting()+ " " + getName();
    }
}

My unit test class is shown below. It illustrates mocking of a final class, a static method and a private method in that class.

import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.powermock.api.mockito.PowerMockito.spy;
import org.powermock.core.classloader.annotations.PowerMockIgnore;

/**
 *
 * @author maarten
 */
@RunWith(PowerMockRunner.class)
@PrepareForTest(App.class)
@PowerMockIgnore("javax.crypto.*")

public class AppTest {
    
    /**
     * Test of helloName method, of class App.
     */
    @Test
    public void testHelloName() throws Exception {
        System.out.println("helloName");
        App instance = spy(new App()); //this allows me to mock private methods
        
        PowerMockito.mockStatic(App.class); //this allows me to mock static methods
        PowerMockito.when(App.getName()).thenReturn("MockedMaarten"); //mocking static method here
        
        PowerMockito.doReturn("MockedHello").when(instance,"getGreeting"); //mocking private method here
        
        String expResult = "MockedHello MockedMaarten";
        String result = instance.helloName();
        assertEquals(expResult, result);
    }
}

For reference and to provide an easy way to get the dependencies, my pom file is as follows. Using properties to specify dependency versions is recommended.

<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>ms.utils</groupId>
    <artifactId>JavaTestProject</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>JavaTestProject</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <mockito.version>1.9.5</mockito.version>
        <powermock.version>1.5.1</powermock.version>
        <junit.version>4.10</junit.version>
        <springframework.version>3.0.6.RELEASE</springframework.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${springframework.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>${mockito.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-api-mockito</artifactId>
            <version>${powermock.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-module-junit4</artifactId>
            <version>${powermock.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

No comments:

Post a Comment