Home » Reflection
Archiv der Kategorie: Reflection
Mocks erzeugen für via Class spezifizierte Objekte
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.
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; } }
Aufrufende Klasse:
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); } }
Feld-Wert via Reflection setzen
With Spring:
org.springframework.test.util.ReflectionTestUtils#setField(java.lang.Object, java.lang.String, java.lang.Object)
Pure Java:
Person person = new Person(); Class<?> c = person.getClass(); Field f = c.getDeclaredField("name"); f.setAccessible(true); f.setBoolean(person, "Hans");