Hoe ongeldige methoden te bespotten met Mockito

Hoe methoden te bespotten met ongeldig retourtype?

Ik heb een waarnemerspatroon geïmplementeerd, maar ik kan er niet mee spotten met Mockito omdat ik niet weet hoe.

En ik heb geprobeerd een voorbeeld op internet te vinden, maar het lukte niet.

Mijn klas ziet er als volgt uit:

public class World {
    List<Listener> listeners;
    void addListener(Listener item) {
        listeners.add(item);
    }
    void doAction(Action goal,Object obj) {
        setState("i received");
        goal.doAction(obj);
        setState("i finished");
    }
    private string state;
    //setter getter state
} 
public class WorldTest implements Listener {
    @Test public void word{
    World  w= mock(World.class);
    w.addListener(this);
    ...
    ...
    }
}
interface Listener {
    void doAction();
}

Het systeem wordt niet geactiveerd met mock.

Ik wil de bovengenoemde systeemstatus laten zien. En op basis daarvan beweringen doen.


Antwoord 1, autoriteit 100%

Bekijk de Mockito API-documenten. Zoals het gekoppelde document vermeldt (punt # 12), kunt u elk van de doThrow(),doAnswer(),doNothing(),doReturn()-familie van methoden van Mockito-framework tot mock void-methoden.

Bijvoorbeeld

Mockito.doThrow(new Exception()).when(instance).methodName();

of als je het wilt combineren met vervolggedrag,

Mockito.doThrow(new Exception()).doNothing().when(instance).methodName();

Ervan uitgaande dat u de setter setState(String s)in de klasse World hieronder bespot, gebruikt de code de doAnswer-methode om de setState.

World mockWorld = mock(World.class); 
doAnswer(new Answer<Void>() {
    public Void answer(InvocationOnMock invocation) {
      Object[] args = invocation.getArguments();
      System.out.println("called with arguments: " + Arrays.toString(args));
      return null;
    }
}).when(mockWorld).setState(anyString());

Antwoord 2, autoriteit 10%

Ik denk dat ik een eenvoudiger antwoord op die vraag heb gevonden, om de echte methode voor slechts één methode aan te roepen (zelfs als deze een ongeldige return heeft), je kunt dit doen:

Mockito.doCallRealMethod().when(<objectInstance>).<method>();
<objectInstance>.<method>();

Of je zou de echte methode kunnen aanroepen voor alle methoden van die klasse, door dit te doen:

<Object> <objectInstance> = mock(<Object>.class, Mockito.CALLS_REAL_METHODS);

Antwoord 3, autoriteit 7%

Toevoegend aan wat @sateesh zei, als je een ongeldige methode wilt bespotten om te voorkomen dat de test deze aanroept, kun je een spyop deze manier gebruiken:

World world = new World();
World spy = Mockito.spy(world);
Mockito.doNothing().when(spy).methodToMock();

Als je je test wilt uitvoeren, zorg er dan voor dat je de methode in test aanroept op het spy-object en niet op het world-object. Bijvoorbeeld:

assertEquals(0, spy.methodToTestThatShouldReturnZero());

Antwoord 4, autoriteit 4%

De oplossing van het zogenaamde probleem is het gebruik van een spyMockito.spy(…)in plaats van een mockMockito.mock(..).

Spy stelt ons in staat om gedeeltelijk te spotten. Mockito is hier goed in. Omdat je een klas hebt die niet compleet is, bespot je op deze manier een vereiste plaats in deze klas.


Antwoord 5, autoriteit 3%

Allereerst: je moet altijd mockito static importeren, op deze manier wordt de code veel leesbaarder (en intuïtiever):

import static org.mockito.Mockito.*;

Voor gedeeltelijk spotten en toch originele functionaliteit behouden voor de rest biedt mockito “Spy”.

Je kunt het als volgt gebruiken:

private World world = spy(new World());

Om te voorkomen dat een methode wordt uitgevoerd, kunt u zoiets als dit gebruiken:

doNothing().when(someObject).someMethod(anyObject());

om wat aangepast gedrag aan een methode te geven, gebruik “when” met een “thenReturn”:

doReturn("something").when(this.world).someMethod(anyObject());

Voor meer voorbeelden vindt u de uitstekende mockito-voorbeelden in het document.


Antwoord 6, autoriteit 2%

Hoe u ongeldige methoden kunt bespotten met mockito – er zijn twee opties:

  1. doAnswer– Als we willen dat onze bespotte ongeldige methode iets doet (het gedrag spotten ondanks dat het ongeldig is).
  2. doThrow– Dan is er Mockito.doThrow()als je een uitzondering wilt maken van de mocked void-methode.

Hier volgt een voorbeeld van hoe het te gebruiken (geen ideale usecase, maar wilde alleen het basisgebruik illustreren).

@Test
public void testUpdate() {
    doAnswer(new Answer<Void>() {
        @Override
        public Void answer(InvocationOnMock invocation) throws Throwable {
            Object[] arguments = invocation.getArguments();
            if (arguments != null && arguments.length > 1 && arguments[0] != null && arguments[1] != null) {
                Customer customer = (Customer) arguments[0];
                String email = (String) arguments[1];
                customer.setEmail(email);
            }
            return null;
        }
    }).when(daoMock).updateEmail(any(Customer.class), any(String.class));
    // calling the method under test
    Customer customer = service.changeEmail("[email protected]", "[email protected]");
    //some asserts
    assertThat(customer, is(notNullValue()));
    assertThat(customer.getEmail(), is(equalTo("[email protected]")));
}
@Test(expected = RuntimeException.class)
public void testUpdate_throwsException() {
    doThrow(RuntimeException.class).when(daoMock).updateEmail(any(Customer.class), any(String.class));
    // calling the method under test
    Customer customer = service.changeEmail("[email protected]", "[email protected]");
}
}

Je kunt meer details vinden over het spottenen testennietigmethoden met Mockito in mijn post Hoe te spotten met Mockito (Een uitgebreide gids met voorbeelden)


Antwoord 7, autoriteit 2%

In Java 8 kan dit een beetje schoner worden gemaakt, ervan uitgaande dat je een statische import hebt voor org.mockito.Mockito.doAnswer:

doAnswer(i -> {
  // Do stuff with i.getArguments() here
  return null;
}).when(*mock*).*method*(*methodArguments*);

De return null;is belangrijk en zonder deze zal het compileren mislukken met een aantal vrij obscure fouten omdat het geen geschikte overschrijving voor doAnswerkan vinden.

Bijvoorbeeld een ExecutorServicedie gewoon onmiddellijk een Runnableuitvoert dat is doorgegeven aan execute()kan worden geïmplementeerd met:

doAnswer(i -> {
  ((Runnable) i.getArguments()[0]).run();
  return null;
}).when(executor).execute(any());

Antwoord 8

Nog een antwoord aan het stel toevoegen (geen woordspeling bedoeld)…

Je moet wel de doAnswer methode aanroepen als je geen spy’s wilt gebruiken. U hoeft echter niet per se uw eigen Antwoordte gebruiken. Er zijn verschillende standaardimplementaties. Met name CallsRealMethods.

In de praktijk ziet het er ongeveer zo uit:

doAnswer(new CallsRealMethods()).when(mock)
        .voidMethod(any(SomeParamClass.class));

Of:

doAnswer(Answers.CALLS_REAL_METHODS.get()).when(mock)
        .voidMethod(any(SomeParamClass.class));

Antwoord 9

Ik denk dat je problemen te wijten zijn aan je teststructuur. Ik vond het moeilijk om spot te combineren met de traditionele methode voor het implementeren van interfaces in de testklasse (zoals je hier hebt gedaan).

Als je de luisteraar als een Mock implementeert, kun je de interactie verifiëren.

Listener listener = mock(Listener.class);
w.addListener(listener);
world.doAction(..);
verify(listener).doAction();

Dit zou u moeten overtuigen dat de ‘Wereld’ het juiste doet.


Antwoord 10

Als je een aantal bewerkingen moet uitvoeren in de mocked void-methode, en je moet het argument manipuleren dat naar de void-methode is verzonden; je kunt Mockito.doAnswer combineren met de ArgumentCaptor.capture-methode.

Stel dat u SpaceServiceheeft waarmee een GalaxyServiceautomatisch wordt aangesloten, met een ongeldige methode genaamd someServiceMethod.

U wilt een test schrijven voor een van uw methoden in SpaceServicedie de void-methode van GalaxyServiceaanroept. Je planeet wordt ook binnen SpaceService gegenereerd. Dus je hebt geen kans om dat te bespotten.

Hier is uw voorbeeld SpaceService-klasse waarvoor u tests wilt schrijven.

class SpaceService {
    @Autowired
    private GalaxyService galaxyService;
    public Date someCoolSpaceServiceMethod() {
        // does something
        Planet planet = new World();
        galaxyService.someServiceMethod(planet); //Planet updated in this method.
        return planet.getCurrentTime();
    }
}

De GalaxyService.someServiceMethodmethode verwacht een planeetargument. Doet wat dingen in de methode. Zie :

GalaxyService {
    public void someServiceMethod(Planet planet) {
        //do fancy stuff here. about solar system etc.
        planet.setTime(someCalculatedTime); // the thing that we want to test.
        // some more stuff.
    }
}

En u wilt deze functie testen.

Hier is een voorbeeld:

ArgumentCaptor<World> worldCaptor = ArgumentCaptor.forClass(World.class);
Date testDate = new Date();
Mockito.doAnswer(mocked-> {
    World capturedWorld = worldCaptor.getValue();
    world.updateTime(testDate);
    return null;
}).when(galaxyService.someServiceMethod(worldCaptor.capture());
Date result = spaceService.someCoolSpaceServiceMethod();
assertEquals(result, testDate);

Antwoord 11

In jouw voorbeeld zou je het Listener-item moeten bespotten en Mockito.verify gebruiken om de interactie ermee te controleren

Other episodes