We learned about testing impediments and how to refactor them. We cannot unit test code when testing impediments are present; we refactor code and move the impediments out (to another class or method), and during testing, the impediments are replaced with mock objects. PowerMock is a dirty solution and it should only be used for legacy code. This is the better way is to refactor the source and make more test friendly.
However, sometimes we cannot mock out the external dependencies due to testing-unfriendly design. This section covers the design for testability, or rather, things to avoid in code. The following Java constructs go up against mocking the testing impediments:
You cannot unit test legacy code because it is either tightly coupled, or testing unfavorable language constructs hide the testing impediments. The following section explains testing unfavorable constructs.
To build a test, we need to instantiate the class in test harness, but the problem with legacy code is that it is difficult to break dependency and instantiate a class in a test harness. For example, in a constructor, the class instantiates many objects, reads from the properties
file, or even creates a database connection. There could be many callers of the class, so you cannot change the constructor to pass dependencies, otherwise it will cause a series of compilation errors.
We will take a look at the legacy code and try to write a test for the class.
Suppose we have a TestingUnfavorableConstructor
class with two external dependencies, DatabaseDependency
and FileReadDependency
. Both the dependencies are slow in nature and testing impediments. The TestingUnfavorableConstructor
class creates dependencies in the constructor. The dependencies represent the database access and the file reads from the TestingUnfavorableConstructor
constructor. The following is the class:
public class TestingUnfavorableConstructor { private DatabaseDependency dependency1; private FileReadDependency dependency2; public TestingUnfavorableConstructor() { this.dependency1 = new DatabaseDependency(); this.dependency2 = new FileReadDependency(); } public Object testMe(Object arg) { return arg; } }
If we want to unit test the testMe()
behavior of the class, we need to create an object for the TestingUnfavorableConstructor
class. However, when we try to create an instant in the unit test, the class fails to indicate that the class cannot be instantiated from an automated test suite. The following is the output:
To overcome this, you should inject the dependencies through the constructor instead of creating them in the constructor.
We cannot modify the default constructor because the class is invoked from many other clients; we cannot break the clients. Two other options are as follows:
The first option is relatively straightforward; we'll apply the second approach.
The following is the modified code:
public class TestingUnfavorableConstructor { private DatabaseDependency dependency1; private FileReadDependency dependency2; public TestingUnfavorableConstructor() { createDependencies(); } protected void createDependencies() { this.dependency1 = new DatabaseDependency(); this.dependency2 = new FileReadDependency(); } public void setDependency1(DatabaseDependency dependency1) { this.dependency1 = dependency1; } public void setDependency2(FileReadDependency dependency2) { this.dependency2 = dependency2; } public Object testMe(Object arg) { return arg; } }
The following unit test overrides the TestingUnfavorableConstructor
class, provides an empty implementation of the createDependencies()
method, creates mock dependencies, and calls setter methods to set the mock dependencies:
@RunWith(MockitoJUnitRunner.class) public class TestingUnfavorableConstructorTest { @Mock DatabaseDependency dep1; @Mock FileReadDependency dep2; TestingUnfavorableConstructor unfavorableConstructor; @Before public void setUp() { unfavorableConstructor= new TestingUnfavorableConstructor() { protected void createDependencies() { } }; unfavorableConstructor.setDependency1(dep1); unfavorableConstructor.setDependency2(dep2); } @Test public void sanity() throws Exception { } }
The empty test method is used to check the health of the test setup, as you need at least one test method to invoke the setup method.
Do not instantiate dependencies in the constructor; the dependencies may exhibit testing impediments and make the class nontestable. Instead of instantiating the dependencies in the constructor, you can pass the real implementations (real dependencies) to the constructor or setter method of the code under the test.
Declaring the class-level variable and instantiating the object at the same time creates a problem; you don't get a chance to mock out the variable. The following example explains the problem.
The VariableInitialization
class has a database dependency and the dependency is instantiated where it is declared:
Public class VariableInitialization { DatabaseDependency dependency1 = new DatabaseDependency(); public void testMe(Object obj) { } }
When you instantiate the VariableInitialization
class in the test, the test fails. The following screenshot shows the output:
Here is the test class:
public class VariableInitializationTest { VariableInitialization initialization; @Before public void setUp() throws Exception { initialization = new VariableInitialization(); } @Test public void sanity() throws Exception { } }
The following are the options to overcome class-level variable initialization:
Do not instantiate the testing impediment variables at the class level. You can still instantiate variables, such as list = new ArrayList<String>()
and more, which are totally reasonable to build internal fields in themselves; it's the difference between coupling to collaborating classes and the internal state.
Private methods are useful for hiding the internal state and encapsulation, but they can also hide the testing impediments. The following example explains the details.
The PrivateMethod
class has a private method showError()
. This private method hides a test impediment. When we unit test the validate()
method with a null object, the validate()
method calls the showError
message:
public class PrivateMethod { public Object validate(Object arg) { if(arg == null) { showError("Null input"); } return arg; } private void showError(String msg) { GraphicalInterface.showMessage(msg); } }
The following is the test output:
You can extract the testing impediments to a protected (or default package visibility) method, or you can separate the concern, create a new class, move the testing impediment to that class, and inject the new class as a dependency. Objects should do one thing; if you've got a method you want to test that does X and is in the same class as a method that does Y (which is so totally different that it can't be allowed to happen in your test for X), then your class must be doing two things. Split these responsibilities.
In this example, validating objects and showing errors are two different responsibilities and should be managed by two different classes.
The following code refactors the testing impediments and makes the class unit testable:
public class PrivateMethodRefactored { public Object validate(Object arg) { if(arg == null) { showError("Null input"); } return arg; } protected void showError(String msg) { GraphicalInterface.showMessage(msg); } }
The showError
method's access specifier is changed to protected
.
The following test code extends the class with an anonymous implementation and overrides the protected method with an empty implementation. The test code invokes the validate()
method on the new anonymous implementation of the PrivateMethodRefactored
class, and in turn, the polymorphic behavior calls the empty implementation. Hence, the test will always bypass the testing impediments by calling the overridden empty implementation of the testing impediment, but the real production code will always invoke the protected method.
public class PrivateMethodRefactoredTest { PrivateMethodRefactored privateMethod; @Before public void setUp() { privateMethod = new PrivateMethodRefactored() { protected void showError(String msg) { } }; } @Test public void validate() throws Exception { privateMethod.validate(null); } }
This approach of bypassing the testing impediments with overridden versions of the testing impediments is known as faking or fake object. If the code under the test contains many testing impediments, it is not possible to override all of them in an anonymous class; rather, we can create an inner class, extend the code under test, and override all testing-unfriendly methods.
When a method is final, you cannot override it. If the final method hides any testing impediment, you cannot unit test the class. The following example demonstrates the issue.
The FinalDependency
class has a final method called doSomething
. This method hides a testing-unfriendly feature. The following is the class definition:
public class FinalDependency { public final void doSomething() { throw new TestingImpedimentException("Final methods cannot be overriden"); } }
The FinalMethodDependency
class has a dependency on FinalDependency
, and in the testMe
method, it calls the doSomething
method:
public class FinalMethodDependency { private final FinalDependency dependency; public FinalMethodDependency(FinalDependency dependency) { this.dependency = dependency; } public void testMe() { dependency.doSomething(); } }
In the test, we'll mock the dependency and unit test the code:
@RunWith(MockitoJUnitRunner.class) public class FinalMethodDependencyTest { @Mock FinalDependency finalDependency; FinalMethodDependency methodDependency; @Before public void setUp() { methodDependency = new FinalMethodDependency(finalDependency); } @Test public void testSomething() throws Exception { methodDependency.testMe(); } }
When we run the test, it still accesses the testing impediment, as the mock object cannot stub a final method. When we try to stub the method, we get an error. The following test stubs the final method call:
@Test public void testSomething() throws Exception { doNothing().when(finalDependency).doSomething(); methodDependency.testMe(); }
When we run the test, we get the following error message thrown by the Mockito framework:
A possible way to overcome this is by extracting the content of the final method to a protected method, calling the protected method from the final method, and overriding the protected method in the test. If you cannot touch the class at all, use the PowerMock framework. For example, when you have only a JAR file, create a MethodDependency
interface with FinalDependency
, implementing it as we have done here, rather than having FindalMethodDependency
depend on MethodDependency
. Then in production, you need to provide a FinalMethodDependency
instance (as done here), but in tests, you can stub the interface happily, which doesn't have any final methods, and you are all set to proceed.
Static methods are good for utility classes but unnecessary use of static can hide the testing impediments and create a problem for unit testing. The following example demonstrates the issue.
The SingletonDependency
class is an implementation of the Gang of Four (GoF) singleton design pattern. It has a private constructor and a static getInstance()
method to create only a single instance of the class. The static callMe()
method hides a testing impediment. Note that the GoF singleton pattern doesn't define methods as static, but in this example, we are defining the callMe()
method as static, to display a drawback of static methods. The following is the singleton implementation:
public class SingletonDependency { private static SingletonDependency singletonDependency; private SingletonDependency() { } public synchronized static SingletonDependency getInstance() { if (singletonDependency == null) { singletonDependency = new SingletonDependency(); } return singletonDependency; } Public static void callMe() { throw new TestingImpedimentException("we dont need singleton"); } }
The VictimOfAPatternLover
class has a dependency on SingletonDependency
. The following are the class details:
public class VictimOfAPatternLover { private final SingletonDependency dependency; public VictimOfAPatternLover(SingletonDependency dependency) { this.dependency = dependency; } public void testMe() { dependency.callMe(); } }
Mockito cannot stub a static method. When we try to stub the static callMe()
method, it still calls the original method and fails for the testing impediment. You cannot stub a static method.
The only way to overcome this issue is to create a protected method and wrap the static call. From the code, call the wrapped method, and from the test, override the protected method. We will now add a wrapper method in the dependency class and call the static method from it:
public static void callMe() { throw new TestingImpedimentException("Come on we dont need singleton"); } protected void wrapper() { callMe(); } }
From the code, call the wrapper method:
public void testMe() { dependency.wrapper(); }
Stub the wrapper method in the test:
@Test public void testMe() throws Exception { Mockito.doNothing().when(dependency).wrapper(); aPatternLover.testMe(); }
The better way to do this is to stop calling the static method from this class entirely and wrap it in a separate class, which you pass in as a dependency.
Say you've got a Database.create()
static method you call from your class A. You could have a DatabaseBuilder
class which you pass into class A, and then just have a call databaseBuilder.create()
, where DatabaseBuilder
is something like the following:
public class DatabaseBuilder { public void create() { Database.create(); } }
And then in the tests, you just provide a stubbed database builder and swap out the whole thing. I would really not recommend using this pattern of making private methods protected and overriding them, except where it's absolutely necessary.
Alternatively, of course, if you can't change the API; you'd use PowerMock to stub the static call.
You cannot override a final class, so you can hide testing-unfavorable features in a final class. The following example explains the problem.
The final class hides a testing impediment:
public final class FinalDepencyClass { public void poison() { throw new TestingImpedimentException("Finals cannot be mocked"); } }
The code under the test has a dependency on the final class:
public class FinalClassDependency { private final FinalDepencyClass finalDepencyClass; public FinalClassDependency(FinalDepencyClass finalDepencyClass) { this.finalDepencyClass = finalDepencyClass; } public void testMe() { finalDepencyClass.poison(); } }
In the test, we'll try to stub the poison
method:
@RunWith(MockitoJUnitRunner.class) public class FinalClassDependencyTest { @Mock FinalDepencyClass finalDependency; FinalClassDependency test; @Before public void setUp() { test = new FinalClassDependency(finalDependency); } @Test public void testMe() throws Exception { Mockito.doNothing().when(finalDependency).poison(); test.testMe(); } }
The test fails with a MockitoException
error as Mockito cannot mock a final class. The following screenshot displays the JUnit output:
Final classes are important for framework or architecture design so that no one can hack the behavior, but it can create a serious problem for unit testing. Before you choose to make a class final, ensure that your final class is a final implementation of some interface so that other clients of the class can potentially use stubbed instances of the interface.
Java instantiates classes using the new
operator, but an innocent new can create a problem for unit testing.
The following example explains the issue. The PoisonIvy
constructor has a testing impediment, for instance, when the method call fetches data from a database table or reads from the filesystem, we represent the testing impediment with the TestingImpedimentException
error:
public class PoisonIvy { public PoisonIvy() { throw new TestingImpedimentException("use dependency injection"); } public void poison() { } }
The following is the code that calls the PoisonIvy
class:
public class NewExpressionDependency { public void testMe() { PoisonIvy ivy = new PoisonIvy(); ivy.poison(); } }
When we unit test the testMe()
code, it fails. The testMe()
method directly creates an instance of dependency and calls the poison()
method. You cannot override this new expression. If we want to unit test the testMe()
method, first we need to move the new
operator outside testMe()
, as we cannot instantiate the PoisonIvy
class. The constructor of the PoisonIvy
class throws an exception, hence we cannot unit test the testMe
behavior unless we move the object creation out of testMe
. Instead of creating a new instance of PoisonIvy
inside testMe()
, we can pass an instance of PoisonIvy
as a method argument, or create a class-level dependency and pass PoisonIvy
as a constructor- or setter-dependency argument.
Program to an interface, not an implementation. Rather than hardcoding the collaborator instantiation of the subtype into the code, assign the concrete collaborator implementation object through a dependency injection. Separate the parts of the codebase that use objects, and implement your logic from the parts that decide which objects to use where (typically with a DI framework such as Guice or Spring).
What does program to an interface, not an implementation mean? It means, program to a super type, rather than a subtype. You can interchange the implementation at runtime. In a collection framework, we have the List
interface and its many implementations. In your class, always define a variable or method return type for List
not ArrayList
, so that if required, you can assign any implementation you want.
In this example, you can pass the PoisonIvy
class as a constructor or setter dependency, and at runtime (during testing), you can pass a mock or a fake implementation to suppress the testing impediments.
Static initializations and static blocks are executed during class loading; you cannot override them. If you initialize a testing impediment in a static block, you cannot unit test the class. The following example demonstrates the issue.
The StaticBlockOwner
class has a static variable known as StaticBlockDependency
, and it initializes the variable in a static block. The following is the class:
public class StaticBlockOwner { private static StaticBlockDependency blockDependency; static { blockDependency = new StaticBlockDependency(); blockDependency.loadTime = new Date(); } public void testMe() { } }
When we unit test the class, it fails. The following is the unit test:
public class StaticBlockOwnerTest { StaticBlockOwner owner; @Before public void setUp() { owner = new StaticBlockOwner(); } @Test public void clean() throws Exception { owner.testMe(); } }
The test fails with java.lang.ExceptionInInitializationError
as it tries to instantiate the dependency in a static block and the dependency throws an exception.