Home » Mockito

Archiv der Kategorie: Mockito

Dynamische Mocking

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 ...

Mockito.when(null) – Return basierend auf equality zu gegebenem Input

Mockito.mock(null)

Weshalb sollte das sinn machen?

Mockito.when(null).thenReturn(meinObjekt)

bedeutet eigentlich: Liebes Mockito. Nimm den letzten Aufruf auf irgend einem Mock. Wenn irgendwann derselbe Aufruf mit denselben Parametern gemacht werden sollte, dann gib „meinObjekt“ zurück.

Dies wird unten demonstriert.

import org.mockito.Mockito;

import java.util.Objects;

/**
 * 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 HPTest {
    public static void main(String[] args) {
        MyObj mockedObject = Mockito.mock(MyObj.class);
        
        /* Tell Mockito, that if get1() is called, then return "super cool string 1.1"*/
        mockedObject.get1();
        Mockito.when(null).thenReturn("super cool string 1.1");
        mockedObject.get2();
        Mockito.when(null).thenReturn("super cool string 2");
        System.out.println(mockedObject.get1());
        System.out.println(mockedObject.get1());
        System.out.println(mockedObject.get2());
        System.out.println(mockedObject.toString());


        /* Tell Mockito, that if return1("1") is called, then return "Return 1" */
        mockedObject.return1("1");
        Mockito.when(null).thenReturn("Return 1");

        /* but if return1("2") is called, then return "Return 2" */
        mockedObject.return1("2");
        Mockito.when(null).thenReturn("Return 2");

        System.out.println(mockedObject.return1("1"));
        System.out.println(mockedObject.return1("2"));
        System.out.println(mockedObject.return1("2"));
        System.out.println(mockedObject.return1("1"));

        mockedObject.returnOnObj(new Bohne("A"));
        Mockito.when(null).thenReturn("On Obj A");

        mockedObject.returnOnObj(new Bohne("B"));
        Mockito.when(null).thenReturn("On Obj B");

        System.out.println(mockedObject.returnOnObj(new Bohne("A")));
        System.out.println(mockedObject.returnOnObj(new Bohne("B")));
        System.out.println(mockedObject.returnOnObj(new Bohne("B")));
        System.out.println(mockedObject.returnOnObj(new Bohne("A")));
    }

}
class MyObj {
    public String get1(){return "not mocked 1";}
    public String get2(){return "not mocked 2";}
    public String return1(String in){return  "not mocked";}
    public String returnOnObj(Bohne in){return  "not mocked";}
}
class Bohne {
    public String id;

    public Bohne(String id) {
        this.id = id;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Bohne)) return false;
        Bohne bohne = (Bohne) o;
        return Objects.equals(id, bohne.id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
}
/*
Output:
super cool string 1.1
super cool string 1.1
super cool string 2
Mock for MyObj, hashCode: 650023597
Return 1
Return 2
Return 2
Return 1
On Obj A
On Obj B
On Obj B
On Obj A

Process finished with exit code 0
 */