Subclass Mock Objects
Jakob Jenkov |
Subclass mock objects is a mock object that is created by subclassing the class you want to test, and overriding some of its methods. Let's look at a simple class:
public class MyUnit { protected MyDependency dependency = null; public MyUnit(MyDependency dep) { this.dependency = dep; } public void doTheThing(String param) { if("one".equals(param)) { dependency.callOne(); } else { dependency.callTwo(); } } }
The class MyUnit
is the class I am trying to unit test. In this unit test, I want to check if
the MyUnit
class calls the MyDependency
class correctly. There are 3 ways to do this:
- Create a Mock implementation of the
MyDependency
class, using a proxy object - Create a subclass mock of the
MyDependency
class, and override thecallOne()
andcallTwo()
methods - Create a subclass of the
MyUnit
class itself, and use that during testing.
In this text I will show you how to do nr. 3. From that method it should be fairly easy to figure out how to do number 2, in case you want that.
Using method nr. 3 requires the following steps:
- Encapsulate calls to
MyDependency
insideMyUnit
- Create a subclass of
MyUnit
- Override the
MyDependency
call encapsualation methods.
I will show you how to perform these three steps, throughout the rest of this text.
Step 1: Encapsulate calls to MyDepedency
The first thing to do is to refactory the MyUnit
class, so that all calls to the
MyDependency
class are encapsulated in their own methods. Here is how that looks:
public class MyUnit { protected MyDependency dependency = null; public MyUnit(MyDependency dep) { this.dependency = dep; } public void doTheThing(String param) { if("one".equals(param)) { callOne(); } else { callTwo(); } } protected void callOne() { dependency.callOne(); } protected void callTwo() { dependency.callTwo(); } }
Notice how the two calls to MyDependency.callOne()
and MyDependency.callTwo()
are
now encapsulated in two protected methods, callOne()
and callTwo()
.
We are now ready to create the MyUnit
subclass.
Step 2: Create a Subclass Mock of MyUnit
The second step is to create a subclass mock of the MyUnit
class. Here it is:
public MyUnitMock extends MyUnit { protected boolean callOneCalled = false; protected boolean callTwoCalled = false; @Override protected void callOne() { this.callOneCalled = true; super.callOne(); } @Override protected void callTwo() { this.callTwoCalled = true; super.callTwo(); } }
Notice how the two overridden methods records if they are called or not, by setting a boolean to true.
The next step is to use the MyUnitMock
in your unit test.
Step 3: Using the MyUnitMock Class in Your Unit Tests
Here is a unit test method that uses the MyUnitMock
class:
@Test public void test() { MyUnitMock myUnitMock = new MyUnitMock(); myUnitMock.doTheThing("one"); assertTrue (myUnitMock.callOneCalled); assertFalse(myUnitMock.callTwoCalled); //reset mock before next call myUnitMock.callOneCalled = false; myUnitMock.doTheThing("two"); assertFalse(myUnitMock.callOneCalled); assertTrue (myUnitMock.callTwoCalled); }
First an instance of the MyUnitMock
class is created. Second, the doTheThing()
method is called. Third, assertions are made about whether the callOne()
and callTwo()
method were invoked.
Summary
As you can see, it is possible to test almost all of a class by using subclass mocks, as described above. There are, however, situations where it works better to use a completely separate mock dependency object with the original class instead. Exactly when, depends on the concrete application.
Just keep in mind, that the above is just a suggestion - one possible method. Use it when it make sense, and don't use it when it doesn't make sense. Use your judgement!
Tweet | |
Jakob Jenkov |