Probleme:
Problem 1: Dynamisches Mocking:
Eine Methode soll Mocks für verschiedene Service-Klassen definieren. In der Methode selbst kann nicht jede in ihr zu Mockende Klasse separat auscodiert werden.
D.h.:
Mockito.when(MeineKlasse.meinMethode())
ist nicht möglich.
Stattdessen wird der Methode ein Class -Objekt übergeben, die den zu mockenden Service definiert.
Problem 2:
Wenn ein bestimmter request vorliegt muss ein definierter Reply zurück gegeben werden.
MyObj2 mock = Mockito.mock(MyObj2.class);
Mockito.when(mock.returnOnObj(new Bohne1("X"))).thenReturn("desired return");
Problem 3 :
Definition einer Sequenz der durch den Mock zurückzugebender Return-Werte.
MyObj2 mock = Mockito.mock(MyObj2.class);
Mockito.when(mock.returnOnObj(Mockito.any())).thenReturn("1").thenReturn("2").thenReturn("3");
Beispiel, das alles obigen Probleme löst:
HPServiceMockFactory.class
import org.mockito.stubbing.OngoingStubbing;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.when;
public class HPServiceMockFactory<T> {
public enum MockMatchingType {IN_SEQUENCE, BY_REQUEST_MATCH};
public <T> T prepareMock(T mock, Class bdType, String svcName, MockMatchingType mockMatchingType, Object[] parameters, Object[] desiredReturns){
Method svc = null;
for (Method method : bdType.getDeclaredMethods()) {
// check if method with this name and number of arguments is declared
if ((method.getParameterTypes().length == parameters.length) && (method.getName().equals(svcName))) {
// check method parameters
if (allinstanceof(parameters, method.getParameterTypes())) {
try {
Object[] paramsForInvocation = new Object[parameters.length];
for(int i = 0; i < parameters.length; i++){
if(mockMatchingType.equals(MockMatchingType.IN_SEQUENCE)){
// When we want to invoke in sequence: First invocation gets desiredReturn[0], second gets desiredReturn[1], ...
// Then we should narrow only to anyObject parameters (or we could narrow the the suiting parameter classes)
paramsForInvocation[i] = anyObject();
}else{
// When we want the mock to return an answer which is defined by its request being equal as the one when the mock is called
// then we have to call the mock and pass the expected parameters BEFORE (farther below) define the return values (via the "thenReturn(..))
paramsForInvocation[i] = parameters[i];
}
}
svc = bdType.getDeclaredMethod(svcName, method.getParameterTypes());
svc.invoke(mock, paramsForInvocation);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Failed to initialize mock data, " + e.toString(), e);
} catch (InvocationTargetException e) {
throw new IllegalStateException("Failed to initialize mock data, " + e.toString(), e);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Failed to initialize mock data, " + e.toString(), e);
}
}
}
}
if (svc == null) {
throw new IllegalStateException("Service method could not be found");
}
OngoingStubbing<Object> ongoingStubbing = when(svc);
for(Object desiredReturn : desiredReturns){
ongoingStubbing = ongoingStubbing.thenReturn(desiredReturn);
}
return mock;
}
private boolean allinstanceof(Object obj[], Class<?> classes[]) {
for (int i = 0; i < obj.length; i++) {
Class<?> clazz = classes[i];
Object o = obj[i];
if (clazz.isPrimitive()) {
if (clazz == boolean.class) {
if (!(o instanceof Boolean)) {
return false;
}
} else if (clazz == int.class) {
if (!(o instanceof Integer)) {
return false;
}
}
} else {
if (o != null && !clazz.isInstance(o)) {
return false;
}
}
}
return true;
}
}
Test Klassen:
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import java.util.Objects;
import static com.ubs.m0m.mock.HPServiceMockFactory.MockMatchingType.BY_REQUEST_MATCH;
import static com.ubs.m0m.mock.HPServiceMockFactory.MockMatchingType.IN_SEQUENCE;
/**
* Demonstrates how Mockito.when(null) can be used to tell Mockito to
* return a given value if objects equals to given ones are passed to the mocked method
*/
public class HPServiceMockTest {
@Test
public void mustReturnMockAnswersAsDefinedInASequence(){
HPService mockedObject = Mockito.mock(HPService.class);
Object[] parameters = new Object[1];
parameters[0] = new Bohne1("X");
String[] desiredReturns = {"Response 1", "Response 2", "Response 3"};
HPServiceMockFactory myServiceMockFactory = new HPServiceMockFactory<HPService>();
myServiceMockFactory.prepareMock(mockedObject, HPService.class, "returnOnObj", IN_SEQUENCE, parameters, desiredReturns);
Assert.assertEquals(desiredReturns[0], mockedObject.returnOnObj(new Bohne1("ANY")));
Assert.assertEquals(desiredReturns[1], mockedObject.returnOnObj(new Bohne1("ANY")));
Assert.assertEquals(desiredReturns[2], mockedObject.returnOnObj(new Bohne1("ANY")));
}
@Test
public void mustReturnMockAnswersThatMatchTheGivenRequest(){
HPServiceMockFactory myServiceMockFactory = new HPServiceMockFactory<HPService>();
HPService mockedObject = Mockito.mock(HPService.class);
Object[] whenReqParams1 = new Object[1];
whenReqParams1[0] = new Bohne1("Request that must match 1");
String[] desiredReturns1 = {"Right Response 1"};
myServiceMockFactory.prepareMock(mockedObject, HPService.class, "returnOnObj", BY_REQUEST_MATCH, whenReqParams1, desiredReturns1);
Object[] whenReqParams2 = new Object[1];
whenReqParams2[0] = new Bohne1("Request that must match 2");
String[] desiredReturns2 = {"Right Response 2"};
myServiceMockFactory.prepareMock(mockedObject, HPService.class, "returnOnObj", BY_REQUEST_MATCH, whenReqParams2, desiredReturns2);
Assert.assertEquals(desiredReturns1[0], mockedObject.returnOnObj(new Bohne1("Request that must match 1")));
Assert.assertEquals(desiredReturns2[0], mockedObject.returnOnObj(new Bohne1("Request that must match 2")));
Assert.assertNull(mockedObject.returnOnObj(new Bohne1("ANY")));
}
}
class HPService {
public String returnOnObj(Bohne1 in){return "not mocked";}
}
class Bohne1 {
public String id;
public Bohne1(String id) {
this.id = id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Bohne1)) return false;
Bohne1 bohne1 = (Bohne1) o;
return Objects.equals(id, bohne1.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
... code ...