Mocking Internal State
I'm looking for a suitable mocking tool to mock internal states (e.g. void methods). I know Powermock and JMock can do this but I have not made a choice yet. I have more experience with EasyMock but have not tried mocking internal states. I'm not so sure if it is inherently supported. I'm s开发者_开发百科till scouring its documentation.
By internal state, i assume you might be meaning some private or package-private method that your public method calls in order to do its job. In my view, if your internally-called method is so complicated that you want to mock it out, then you should really pull it out into it's own class, test that class separately, and then inject it as a service to the original class you wanted to test as a service.
So your class looks something like this and you want to test someMethod
.
public class Original {
public void someMethod() {
// ... stuff
Object someValue = callComplicatedPrivateMethod();
// ... more stuff
}
}
I would change it to this:
public class Original {
private ComplicatedService service;
public Original(ComplicatedService service) {
this.service = service;
}
public void someMethod() {
// ... stuff
Object someValue = service.callComplicatedMethod();
// ... more stuff
}
}
Now you can easily (normally) mock ComplicatedService
and test your Original
class as normal.
Let's say you have a class like this where you've got an annoying bit of internal state (which is access by a method called doSomething
) and it's stopping you testing myMethod
.
public class Me {
public int myMethod () {
doSomething();
return 7;
}
void doSomething() {
// Access some internal state which is hard to mock
}
}
You can deal with this by just overriding that in your test.
@Test
public void testMyMethod () {
Me fixture = new Me() {
@Override
void doSomething() {
}
}
Assert.assertEquals(7, fixture.myMethod());
}
Using Mockito you can achieve the same with spy
and doAnswer
. For example.
Me me = new Me(); // the original fixture
Me mySpy = spy(me); // a spied version when you can copy the behaviour
doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock inv) throws Throwable {
throw new Exception("Hello!");
}
}).when(mySpy).doSomething();
// Will throw an exception or obviously whatever you put in the body of the answer method
mySpy.doSomething();
I had recently implemented the mocking of internal methods with EasyMock for a project we were working on. This was implemented following the EasyMock documentation. See section Partial Mocking in the EasyMock documentation.
Here is how I wrote the unit test:
Lets assume that an API getTopNVideosByTagNameAndProperty
in class VideoAPI
need to be unit tested and this method internally calls another public method in the same class getVideosByTagName
. The internal method takes in a parameter and returns a List. The method getVideosByTagName
has to be mocked with EasyMock's mock builder.
CODE:
//create the mock object with the help of Mock Builder and then add the method to be mocked.
VideoAPI videoApi=EasyMock.createMockBuilder(Video.class).addMockedMethod("getVideosByTagName").createMock();
String tagName ="sometag"; // create the paramter
// create a dummy return object you expect from the internal method getVideosByTagName
List listVideos = new ArrayList();
for (int i = 1; i <=10; i++) { //populate listVideos }
//set up the EasyMock for the mocked method call
EasyMock.expect(videoApi.getVideosByTagName(tagName)).andReturn(listVideos).anyTimes();
EasyMock.replay(videoApi);
//now call the actual method to be tested - in this case it returns top 5 videos
List<Video> listVideoTopN = videoApi.getTopNVideosByTagNameAndProperty("tag", "likes", 5);
//do the asserts
assertNotNull(listVideoTopN);
assertEquals(5, listVideoTopN.size());
//finally verify the mocks
EasyMock.verify(videoApi);
This is working perfectly fine for me.
精彩评论