Esempio dell'argomento di MockitoCaptor


141

Qualcuno può fornirmi un esempio che mostra come utilizzare la org.mockito.ArgumentCaptorclasse e in che modo è diversa dai semplici abbinatori forniti con mockito.

Ho letto i documenti mockito forniti ma quelli non lo illustrano chiaramente, nessuno di loro è in grado di spiegarlo con chiarezza.

Risposte:


187

Sono d'accordo con quello che ha detto @fge, più oltre. Vediamo un esempio. Considera di avere un metodo:

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

Ora, se si desidera controllare i dati interni, è possibile utilizzare Captor:

// 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);

Se doSomething(data)muta innerData, allora quel cambiamento sarà presente assertEquals("Some inner data", actual.innerData)o sarà innerDatacatturato così com'è prima che doSomething venga eseguito?
Cory Klein,

@CoryKlein The OtherClassè una beffa e, come è definito ora doSomething(), non farà nulla, registra semplicemente l'oggetto che è stato passato. Ciò significa che verrà catturato così com'è prima che doSomethingvenga eseguito.
Slava Shpitalny,

3
In verify, times(1)è il difetto e può essere omesso.
Inego,

come fa ArgumentCaptor a sapere che foo (altro) è accaduto poiché è istanziato solo dopo la chiamata foo (altro)?
AvramPop il

1
@AvramPop colui che sa che questo è l'oggetto finto. Contiene molte informazioni sulla simulazione. All'interno di tutte queste informazioni contiene anche la cronologia delle chiamate per ciascun metodo con i suoi parametri. Quando si chiama il verifymetodo, utilizza tali informazioni per eseguire corrispondenze con la verifica che si sta eseguendo. Per ogni parametro chiede se corrisponde alla chiamata specifica che controlla. Quando ArgumentCaptor è selezionato, memorizza semplicemente i valori con cui è stato invocato, quindi quando verifytermina, contiene tutte le invocazioni rilevanti. È più o meno come funziona. Spero che aiuti
Slava Shpitalny il

35

Le due differenze principali sono:

  • quando catturi anche un singolo argomento, sei in grado di fare test molto più elaborati su questo argomento e con un codice più ovvio;
  • è ArgumentCaptorpossibile catturare più di una volta.

Per illustrare quest'ultimo, supponiamo di avere:

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

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

Quindi il Captor sarà in grado di darti accesso a tutti e 4 gli argomenti, sui quali potrai quindi eseguire asserzioni separatamente.

Questo o qualsiasi numero di argomenti in effetti, poiché a VerificationModenon è limitato a un numero fisso di invocazioni; in ogni caso, il rapitore ti darà accesso a tutti, se lo desideri.

Ciò ha anche il vantaggio che tali test sono (imho) molto più facili da scrivere rispetto al dover implementare i tuoi messaggi ArgumentMatcher, in particolare se combini mockito con assertj.

Oh, e per favore considera di usare TestNG invece di JUnit.


1
Cosa succede se ci sono più parametri passati nel metodo - tutti di diversi tipi? Come si verifica effettivamente che il parametro booleano fosse vero , ad esempio.
IgorGanapolsky,

21
Puoi fornire una spiegazione per il tuo commento: Oh, e considera di usare TestNG invece di JUnit. . Perché considerarlo? Perché cambiare
Navigatron

1
@IgorGanapolsky hai appena aggiunto un altro ArgumentCaptor. ArgumentCaptor <BigDecimal> arg = ArgumentCaptor.forClass (BigDecimal.class); ArgumentCaptor <String> arg2 = ArgumentCaptor.forClass (String.class); Michael michael = new Michael (); michael.sayHi (j); verifica (j) .saySomething (arg.capture (), arg2.capture ()); System.out.println ("value is" + arg.getValue ()); System.out.println ("string is" + arg2.getValue ());
johnwick0831,

12

I passaggi per effettuare un controllo completo sono:

Preparare il rapitore:

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

verificare che la chiamata a dipendente dal componente (collaboratore dell'argomento in prova) volte (1) sia il valore predefinito, quindi non è necessario aggiungerlo.

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

Ottieni l'argomento passato al collaboratore

SomeArgumentClass someArgument = messageCaptor.getValue();

someArgument può essere utilizzato per affermazioni


-2

Qui ti sto dando un esempio corretto di un metodo di callback. quindi supponiamo di avere un metodo come metodo 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");
}

Ho anche messo tutta la classe helper qui per rendere più chiaro l'esempio: loginService class

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);
}}

e abbiamo listener LoginListener come:

interface LoginListener {
void onLoginSuccess();

void onLoginFaliure();

}

ora volevo solo testare il metodo login () della 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);

inoltre, non dimenticare di aggiungere annotazioni sopra la classe test come

@RunWith(PowerMockRunner.class)
@PrepareForTest(Login.class)

1
Non dovrebbe fare riferimento a ArgumentCaptor?
Felipe Martins Melo,

sì, stiamo acquisendo il listener passato al metodo login () nell'esempio login (LoginProvider loginProvider, LoginListener callback)
Vikram singh

Dov'è captordefinito nella tua risposta?
tom_mai78101

ArgumentCaptor <LoginListener> listenerCaptor = ArgumentCaptor.forClass (LoginListener.class);
Vikram singh,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.