Exemple de l'argument de MockitoCaptor

141

Quelqu'un peut-il s'il vous plaît me fournir un exemple montrant comment utiliser la org.mockito.ArgumentCaptorclasse et en quoi elle est différente des simples matchers fournis avec mockito.

J'ai lu les documents mockito fournis mais ceux-ci ne l'illustrent pas clairement, aucun d'entre eux n'est capable de l'expliquer avec clarté.

Ujjwal
la source

Réponses:

187

Je suis d'accord avec ce que @fge a dit, plus. Regardons l'exemple. Considérez que vous avez une méthode:

class A {
    public void foo(OtherClass other) {
        SomeData data = new SomeData("Some inner data");
        other.doSomething(data);
    }
}

Maintenant, si vous souhaitez vérifier les données internes, vous pouvez utiliser le capteur:

// Create a mock of the OtherClass
OtherClass other = mock(OtherClass.class);

// Run the foo method with the mock
new A().foo(other);

// Capture the argument of the doSomething function
ArgumentCaptor<SomeData> captor = ArgumentCaptor.forClass(SomeData.class);
verify(other, times(1)).doSomething(captor.capture());

// Assert the argument
SomeData actual = captor.getValue();
assertEquals("Some inner data", actual.innerData);
Slava Shpitalny
la source
Si doSomething(data)mutate innerData, alors ce changement sera-t-il présent assertEquals("Some inner data", actual.innerData)ou sera-t- innerDatail capturé tel quel avant doSomething son exécution?
Cory Klein
@CoryKlein Le OtherClassest un simulacre, et comme il est défini maintenant, doSomething()il ne fera rien, il enregistre simplement l'objet qui a été passé. Cela signifie qu'il sera capturé tel quel avant doSomethingson exécution.
Slava Shpitalny
3
Dans verify, times(1)est la valeur par défaut et peut être omis.
Inego
Comment ArgumentCaptor sait-il que foo (autre) s'est produit puisqu'il n'est instancié qu'après l'appel de foo (autre)?
AvramPop le
1
@AvramPop celui qui sait que c'est l'objet simulé. Il contient à l'intérieur de nombreuses informations sur la maquette. À l'intérieur de toutes ces informations, il contient également l'historique des appels pour chaque méthode avec ses paramètres. Lorsque vous appelez la verifyméthode, elle utilise ces informations pour exécuter des correspondances avec la vérification que vous effectuez. Pour chaque paramètre, il demande s'il correspond à l'appel spécifique qu'il vérifie. Lorsque ArgumentCaptor est coché, il stocke simplement les valeurs avec lesquelles il a été appelé, donc à la verifyfin, il contient toutes les invocations pertinentes. C'est à peu près comment cela fonctionne. J'espère que ça aide
Slava Shpitalny
35

Les deux principales différences sont:

  • lorsque vous capturez ne serait-ce qu'un seul argument, vous êtes capable de faire des tests beaucoup plus élaborés sur cet argument, et avec un code plus évident;
  • un ArgumentCaptorpeut capturer plus d'une fois.

Pour illustrer ce dernier, disons que vous avez:

final ArgumentCaptor<Foo> captor = ArgumentCaptor.forClass(Foo.class);

verify(x, times(4)).someMethod(captor.capture()); // for instance

Ensuite, le capteur pourra vous donner accès aux 4 arguments, sur lesquels vous pourrez ensuite effectuer des assertions séparément.

Ceci ou n'importe quel nombre d'arguments en fait, puisque a VerificationModen'est pas limité à un nombre fixe d'appels; dans tous les cas, le ravisseur vous donnera accès à tous, si vous le souhaitez.

Cela présente également l'avantage que de tels tests sont (à mon humble avis) beaucoup plus faciles à écrire que d'avoir à implémenter vos propres ArgumentMatchers - en particulier si vous combinez mockito avec assertj.

Oh, et pensez à utiliser TestNG au lieu de JUnit.

fge
la source
1
Que faire s'il y a plusieurs paramètres passés dans la méthode - tous de types différents? Comment vérifier réellement que le paramètre booléen était vrai , par exemple.
IgorGanapolsky
21
Pouvez-vous fournir une explication à votre commentaire: Oh, et veuillez envisager d'utiliser TestNG au lieu de JUnit. . Pourquoi le considérer? Pourquoi changer?
Navigatron
1
@IgorGanapolsky vous ajoutez simplement un autre ArgumentCaptor. ArgumentCaptor <BigDecimal> arg = ArgumentCaptor.forClass (BigDecimal.class); ArgumentCaptor <String> arg2 = ArgumentCaptor.forClass (String.class); Michael Michael = nouveau Michael (); michael.sayHi (j); verify (j) .saySomething (arg.capture (), arg2.capture ()); System.out.println ("value is" + arg.getValue ()); System.out.println ("string is" + arg2.getValue ());
johnwick0831
12

Les étapes pour effectuer un contrôle complet sont les suivantes:

Préparez le capteur:

ArgumentCaptor<SomeArgumentClass> someArgumentCaptor = ArgumentCaptor.forClass(SomeArgumentClass.class);

Vérifiez que l'appel à dépendant du composant (collaborateur du sujet testé) times (1), est la valeur par défaut, il n'est donc pas nécessaire de l'ajouter.

verify(dependentOnComponent, times(1)).send(someArgumentCaptor.capture());

Faire passer l'argument au collaborateur

SomeArgumentClass someArgument = messageCaptor.getValue();

someArgument peut être utilisé pour les assertions

Lho Ben
la source
-2

Ici, je vous donne un exemple approprié d'une méthode de rappel. supposons donc que nous ayons une méthode comme la méthode login ():

 public void login() {
    loginService = new LoginService();
    loginService.login(loginProvider, new LoginListener() {
        @Override
        public void onLoginSuccess() {
            loginService.getresult(true);
        }

        @Override
        public void onLoginFaliure() {
            loginService.getresult(false);

        }
    });
    System.out.print("@@##### get called");
}

J'ai également mis toute la classe d'assistance ici pour rendre l'exemple plus clair: classe loginService

public class LoginService implements Login.getresult{
public void login(LoginProvider loginProvider,LoginListener callback){

    String username  = loginProvider.getUsername();
    String pwd  = loginProvider.getPassword();
    if(username != null && pwd != null){
        callback.onLoginSuccess();
    }else{
        callback.onLoginFaliure();
    }

}

@Override
public void getresult(boolean value) {
    System.out.print("login success"+value);
}}

et nous avons l'auditeur LoginListener comme:

interface LoginListener {
void onLoginSuccess();

void onLoginFaliure();

}

maintenant je voulais juste tester la méthode login () de la classe Login

 @Test
public void loginTest() throws Exception {
    LoginService service = mock(LoginService.class);
    LoginProvider provider = mock(LoginProvider.class);
    whenNew(LoginProvider.class).withNoArguments().thenReturn(provider);
    whenNew(LoginService.class).withNoArguments().thenReturn(service);
    when(provider.getPassword()).thenReturn("pwd");
    when(provider.getUsername()).thenReturn("username");
    login.getLoginDetail("username","password");

    verify(provider).setPassword("password");
    verify(provider).setUsername("username");

    verify(service).login(eq(provider),captor.capture());

    LoginListener listener = captor.getValue();

    listener.onLoginSuccess();

    verify(service).getresult(true);

n'oubliez pas non plus d'ajouter une annotation au-dessus de la classe de test comme

@RunWith(PowerMockRunner.class)
@PrepareForTest(Login.class)
Vikram Singh
la source
1
Ne devrait-il pas faire référence à ArgumentCaptor?
Felipe Martins Melo
oui, nous capturons l'auditeur passé à la méthode login () dans l'exemple de connexion (LoginProvider loginProvider, LoginListener callback)
Vikram singh
Où est le captordéfini dans votre réponse?
tom_mai78101
ArgumentCaptor <LoginListener> listenerCaptor = ArgumentCaptor.forClass (LoginListener.class);
Vikram singh le