Often as developers, we need to add unit testing to legacy code while being unable to make non-trivial changes to the code under test.
In legacy code, logging and other resources are often set up in a static initializer. That code won’t execute nicely in a unit testing environment (could be using JNDI or something else that doesn’t work nicely with unit tests without mocking).
We can’t just add mock behavior to our class instance in the unit test, because the static initializer is run as soon as the class is accessed.
If we’re using Mockito with Powermock the solution to this problem is simple: we need to suppress the static initializer from executing.
Let’s take a look at a simple example of suppressing a static initializer. We have a class ClassWithStaticInit, where we’re assigning a string field in our static initializer to “abc”.
public class ClassWithStaticInit {
final static String field;
static {
//this block could contain undesirable logging setup that doesn't work in a test environment
field = "abc";
}
public String getField(){
return field;
}
}
In the following test, we suppress the static initializer for ClassWithStaticInit using the @SuppressStaticInitializationFor annotation. You can see the return value of the method unit.getField() is null, because we have suppressed the static initializer of ClassWithStaticInit, preventing the field from being set.
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.junit.Assert.assertEquals;
@RunWith(PowerMockRunner.class)
@SuppressStaticInitializationFor({"ClassWithStaticInit"})
@PrepareForTest({ClassWithStaticInit.class})
public class SuppressStaticInitTest {
@InjectMocks
ClassWithStaticInit unit = new ClassWithStaticInit();
@Test
public void testSuppressingStaticInitializer(){
assertEquals(unit.getField(), null);
}
}
Now, if we don’t suppress the static initializer, we can see that the static block has executed, assigning the value “abc” to field.
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.junit.Assert.assertEquals;
@RunWith(PowerMockRunner.class)
@PrepareForTest({ClassWithStaticInit.class})
public class DoNotSuppressStaticInitTest {
@InjectMocks
ClassWithStaticInit unit = new ClassWithStaticInit();
@Test
public void testSuppressingStaticInitializer(){
assertEquals(unit.getField(), "abc");
}
}
pom.xml file
<?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.french</groupId>
<artifactId>MockStaticInitilizerMockito</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>4.13</junit.version>
<mockito.version>2.28.2</mockito.version>
<powermock.version>2.0.7</powermock.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.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</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>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>