dependencyManagement tag in Maven

One very peculiar issue I’ve run into during development is an issue where I have a parent pom.xml file and a child pom.xml file. The child pom.xml file imports the parent artifact as a dependency.

Now, when we make changes to dependencies in the parent pom.xml file, we don’t see them reflected when building the child pom.xml file, the child is still pulling in an unwanted dependency version from the parent.

What’s the issue?

Is the parent artifact being published to the local maven repository correctly? Yes it is, that’s not the issue.

The problem was that our child pom.xml had a <dependencyManagement> tag that was over-writing the changes in the parent pom. Check out this stack overflow post to learn more.

Update 9/30/21: Better explanation can be found here.

The moral of the story: Be aware of what the <dependencyManagement> tag does when debugging maven issues.

Mocking Static Singletons in Java (Mockito 3.4+, EasyMock, JMockit)

A common problem for Java developers that wish to get comprehensive unit test coverage is handling the mocking of singletons that are implemented using static method calls.

Let’s look at how we can mock singleton behavior using three common Java testing libraries Mockito, EasyMock and JMockit.

Part 1: Write Code to Test

To start with, we need a simple contrived example. In this example, we have a class ClassUnderTest that accesses a singleton Singleton and then, based on the return value, calls a method in DummyApiClient.

The issue is, we need to be able to mock the behavior of the static instance of Singleton in order to test ClassUnderTest.

public class ClassUnderTest {

    private DummyApiClient dummyApiClient;

    public ClassUnderTest(DummyApiClient dummyApiClient){
        this.dummyApiClient = dummyApiClient;
    }

    public void methodUnderTest(){
        if(Singleton.getSingleton().getBool()){
            dummyApiClient.doPositive();
        } else {
            dummyApiClient.doNegative();
        }
    }
}
import java.util.Random;

public class Singleton {
    static Singleton singleton;
    static {
        singleton = new Singleton();
    }

    public static Singleton getSingleton(){
        return singleton;
    }

    public Boolean getBool(){
        return new Random().nextBoolean();
    }
}
public class DummyApiClient {
    public void doPositive(){ System.out.println("positive");};
    public void doNegative(){ System.out.println("negative");};
}

Full Part 1 Code on Github

Part 2: Mockito (mockito-inline) 3.4+

Wow! It’s now possible to mock static methods with mockito, without the additional dependency of PowerMock! Since version 3.4 of Mockito (PR), we can mock static methods using the mockStatic command. (examples)

First, let’s add the required dependencies to our pom.xml file. We need to use JUnit and mockito-inline (regular mockito-core will not work).

<?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>org.example</groupId>
    <artifactId>SingletonMockDemo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <maven.surefire.version>2.22.2</maven.surefire.version>
        <junit.version>5.6.2</junit.version>
        <mockito.version>3.4.0</mockito.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven.surefire.version}</version>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-inline</artifactId>
            <version>${mockito.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

Next, let’s write our test class, testing the behavior of both true and false from our singleton Singleton.

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;

import static org.mockito.Mockito.*;

public class ClassUnderTestTest {

    DummyApiClient dummyApiClient;
    Singleton singletonMock;

    ClassUnderTest unit;

    @BeforeEach
    public void beforeEach(){
        dummyApiClient = mock(DummyApiClient.class);
        singletonMock = mock(Singleton.class);

        unit = new ClassUnderTest(dummyApiClient);
    }

    @Test
    public void testMethodUnderTestPositive(){
        when(singletonMock.getBool()).thenReturn(true);

        try (MockedStatic<Singleton> staticSingleton = mockStatic(Singleton.class)) {
            staticSingleton.when(Singleton::getSingleton).thenReturn(singletonMock);

            unit.methodUnderTest();

            staticSingleton.verify(Singleton::getSingleton);
            verify(singletonMock).getBool();
            verify(dummyApiClient).doPositive();
        }
    }

    @Test
    public void testMethodUnderTestNegative(){
        when(singletonMock.getBool()).thenReturn(false);

        try (MockedStatic<Singleton> staticSingleton = mockStatic(Singleton.class)) {
            staticSingleton.when(Singleton::getSingleton).thenReturn(singletonMock);

            unit.methodUnderTest();

            staticSingleton.verify(Singleton::getSingleton);
            verify(singletonMock).getBool();
            verify(dummyApiClient).doNegative();
        }
    }
}

Full Part 2 Code on GitHub

Part 3: EasyMock

As of the time of writing, EasyMock does not support mocking of static methods without the use of an additional library; In our case, we will use PowerMock to support mocking static methods.

First, let’s update our pom.xml to reflect the libraries we are using:

<?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>org.example</groupId>
    <artifactId>SingletonMockDemo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <maven.surefire.version>2.22.2</maven.surefire.version>
        <powermock.version>2.0.2</powermock.version>
        <junit.version>4.13</junit.version>
        <easymock.version>4.2</easymock.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven.surefire.version}</version>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.easymock</groupId>
            <artifactId>easymock</artifactId>
            <version>${easymock.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-module-junit4</artifactId>
            <version>${powermock.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-api-easymock</artifactId>
            <version>${powermock.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

Next, let’s write out test class ClassUnderTestTest.java

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import static org.easymock.EasyMock.expect;
import static org.powermock.api.easymock.PowerMock.mockStatic;

@RunWith(PowerMockRunner.class)
@PrepareForTest(Singleton.class)
public class ClassUnderTestTest {

    DummyApiClient dummyApiClient;
    Singleton singletonMock;


    ClassUnderTest unit;

    @Before
    public void beforeEach(){
        dummyApiClient = PowerMock.createMock(DummyApiClient.class);
        singletonMock = PowerMock.createMock(Singleton.class);
        mockStatic(Singleton.class);
        expect(Singleton.getSingleton()).andReturn(singletonMock);

        unit = new ClassUnderTest(dummyApiClient);
    }

    @Test
    public void testMethodUnderTestPositive(){
        expect(singletonMock.getBool()).andReturn(true);
        dummyApiClient.doPositive();
        PowerMock.expectLastCall();
        PowerMock.replayAll();

        unit.methodUnderTest();
    }

    @Test
    public void testMethodUnderTestNegative(){
        expect(singletonMock.getBool()).andReturn(false);
        dummyApiClient.doNegative();
        PowerMock.expectLastCall();
        PowerMock.replayAll();

        unit.methodUnderTest();
    }
}

Resetting Mocks: Note how mockStatic(Singleton.class); doesn’t have a corresponding reset call. This is because PowerMock resets mocks using the @PrepareForTest annoation. (stack overflow post)

Full Part 3 Code on GitHub

Part 4: JMockit

Again, we must first update our pom.xml file to pull in our required dependencies. You need to update the surefire configuration to use the javaagent initilization parameter.

<?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>org.example</groupId>
    <artifactId>SingletonMockDemo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <maven.surefire.version>2.22.2</maven.surefire.version>
        <junit.version>5.6.2</junit.version>
        <jmockit.version>1.49</jmockit.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven.surefire.version}</version>
                <configuration>
                    <argLine>
                        -javaagent:${settings.localRepository}/org/jmockit/jmockit/${jmockit.version}/jmockit-${jmockit.version}.jar
                    </argLine>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jmockit</groupId>
            <artifactId>jmockit</artifactId>
            <version>${jmockit.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

And, then we need to write our test class, ClassUnderTestTest, this time using JMockit.

import mockit.Expectations;
import mockit.Mocked;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class ClassUnderTestTest {
    
    @Mocked
    DummyApiClient dummyApiClient;

    @Mocked
    Singleton singletonMock;

    ClassUnderTest unit;

    @BeforeEach
    public void beforeEach(){
        unit = new ClassUnderTest(dummyApiClient);
    }

    @Test
    public void testMethodUnderTestPositive(){
        new Expectations(){{
            singletonMock.getBool(); result = true;
            Singleton.getSingleton(); result = singletonMock;
            dummyApiClient.doPositive();
        }};

        unit.methodUnderTest();
    }


    @Test
    public void testMethodUnderTestNegative(){
        new Expectations(){{
            singletonMock.getBool(); result = false;
            Singleton.getSingleton(); result = singletonMock;
            dummyApiClient.doNegative();
        }};

        unit.methodUnderTest();
    }
}

Full Part 4 Code on GitHub

package.json scripts

When using npm (commonly with nodejs), we can pre-define command line commands in the package.json file, using the scripts option.

Use Case: Pre-Standardized Commands Per Organization

Description: Allow teams to standardize commands to allow easy cross-project development and collaboration.

In our example, we will standardize a command extended-test that should be run by all developers before issuing a pull request.

Example package.json for mocha project
{
  "scripts": {
    "extended-test": "echo \"Doing Custom Stuff First\" && mocha"
  },
  "devDependencies": {
    "mocha": "^8.0.1"
  }
}
Example package.json for jest project
{
  "scripts": {
    "extended-test": "echo \"Doing Custom Stuff First\" && jest"
  },
  "devDependencies": {
    "jest": "^26.1.0"
  }
}
Example Invocation (common)
npm run extended-test

Benefit: Developers can abstract away specifics of processes that are common between multiple projects.

Note: In a more mature CI-CD environment, developers would not need to run these commands manually, it would be part of an automated process.

Use Case: Pre-Define Command Line Arguments for Each Environment

{
  "scripts": {
    "report-local": "node tps-report.js --db=\"jdbc:mysql://local-address\" --dry_run=false",
    "report-test": "node tps-report.js --db=\"jdbc:mysql://test-address\" --dry_run=false",
    "report-prod": "node tps-report.js --db=\"jdbc:mysql://prod-address\" --dry_run=true"
  },
  "dependencies": {
    "yargs": "^15.4.1"
  }
}
npm run report-local

This example package.json demonstrates use of the yargs package to pre-define command-line arguments to a script.

This helps prevent user error and allows invocation parameters to be reviewed as part of a pull request, rather than passed as tribal knowledge.