Verificar valor do atributo do objeto com mockito

264

Eu tenho uma chamada de método que eu quero zombar com mockito. Para começar, criei e injetei uma instância de um objeto no qual o método será chamado. Meu objetivo é verificar um dos objetos na chamada de método.

Existe uma maneira de o mockito permitir afirmar ou verificar o objeto e seus atributos quando o método mock é chamado?

exemplo

Mockito.verify(mockedObject)
       .someMethodOnMockedObject(
              Mockito.<SomeObjectAsArgument>anyObject())

Em vez de fazer, anyObject()quero verificar se o objeto de argumento contém alguns campos específicos

Mockito.verify(mockedObject)
       .someMethodOnMockedObject(
              Mockito.<SomeObjectAsArgument>**compareWithThisObject()**)
Priyank
fonte
Como alternativa ao uso do mockito nesses casos, considere criar um stub personalizado que estenda a classe do mockedObject e substitua someMethodOnMockedObject para salvar o objeto para comparação posterior.
que você

Respostas:

540

O novo recurso adicionado ao Mockito torna isso ainda mais fácil,

ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);
verify(mock).doSomething(argument.capture());
assertEquals("John", argument.getValue().getName());

Veja a documentação do Mockito


Caso haja mais de um parâmetro e a captura de apenas um único parâmetro seja desejada, use outros ArgumentMatchers para agrupar o restante dos argumentos:

verify(mock).doSomething(eq(someValue), eq(someOtherValue), argument.capture());
assertEquals("John", argument.getValue().getName());
iraSenthil
fonte
1
se o seu método tiver mais de um argumento, você deverá usar o Matchers para todos os outros argumentos também. akcasoy.wordpress.com/tag/argumentcaptor
robsonrosa
1
E se houver vários argumentos? Como você especifica exatamente o que você está interessado?
IgorGanapolsky
2
@IgorGanapolsky Supondo que um segundo parâmetro String para doSomething você precise fazer: verifique (mock) .doSomething (argument.capture (), anyString ());
GreenTurtle 7/04
a necessidade de usar correspondentes para todos os argumentos é exclusivamente de acordo com as especificações padrão de uso do tipo "tudo ou nenhum".
Charney Kaye
54

Eu acho que a maneira mais fácil de verificar um objeto de argumento é usar o refEqmétodo:

Mockito.verify(mockedObject).someMethodOnMockedObject(Matchers.refEq(objectToCompareWith));

Pode ser usado mesmo se o objeto não for implementado equals(), porque a reflexão é usada. Se você não quiser comparar alguns campos, basta adicionar seus nomes como argumentos pararefEq .

John29
fonte
1
que é uma maneira muito elegante, mas infelizmente org.mockito.Matchers agora está obsoleto
ihebiheb
5
@ihebiheb Foi movido para ArgumentMatchers
Michael #
48

Mais uma possibilidade, se você não quiser usar ArgumentCaptor(por exemplo, porque você também está usando stubbing), é usar o Hamcrest Matchers em combinação com o Mockito.

import org.mockito.Mockito
import org.hamcrest.Matchers
...

Mockito.verify(mockedObject).someMethodOnMockedObject(MockitoHamcrest.argThat(
    Matchers.<SomeObjectAsArgument>hasProperty("propertyName", desiredValue)));
charleyc
fonte
2
Nota: verifique se o Matcherspacote está correto, pois a gravação da mesma linha de código com a org.mockito.Matchersclasse gera uma exceção enganosa, afirmando que o parâmetro da função mock simplesmente não corresponde.
buer
1
Por favor, note que nas versões modernas do Mockito, é MockitoHamcrest.argThat()e não éMockito.argThat()
Roman Puchkovskiy 17/07/19
17

Esta é uma resposta baseada na resposta do iraSenthil, mas com anotação ( Captor ). Na minha opinião, tem algumas vantagens:

  • é mais curto
  • é mais fácil de ler
  • ele pode manipular genéricos sem avisos

Exemplo:

@RunWith(MockitoJUnitRunner.class)
public class SomeTest{

    @Captor
    private ArgumentCaptor<List<SomeType>> captor;

    //...

    @Test 
    public void shouldTestArgsVals() {
        //...
        verify(mockedObject).someMethodOnMockedObject(captor.capture());

        assertThat(captor.getValue().getXXX(), is("expected"));
    }
}
Walery Strauch
fonte
Isso funcionará apenas para um único argumento nos parâmetros.
IgorGanapolsky
Você pode usar um captador para mais de um argumento. Se você capturar mais de um argumento, poderá listar todos os resultados com captor.getAllValues(). O método captor.getValue()usado na resposta fornece o último resultado.
Walery Strauch 24/02
11

Se você estiver usando Java 8, poderá usar expressões Lambda para corresponder.

import java.util.Optional;
import java.util.function.Predicate;

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;

public class LambdaMatcher<T> extends BaseMatcher<T>
{
    private final Predicate<T> matcher;
    private final Optional<String> description;

    public LambdaMatcher(Predicate<T> matcher)
    {
        this(matcher, null);
    }

    public LambdaMatcher(Predicate<T> matcher, String description)
    {
        this.matcher = matcher;
        this.description = Optional.ofNullable(description);
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean matches(Object argument)
    {
        return matcher.test((T) argument);
    }

    @Override
    public void describeTo(Description description)
    {
        this.description.ifPresent(description::appendText);
    }
}

Chamada de exemplo

@Test
public void canFindEmployee()
{
    Employee employee = new Employee("John");
    company.addEmployee(employee);

    verify(mockedDal).registerEmployee(argThat(new LambdaMatcher<>(e -> e.getName()
                                                                         .equals(employee.getName()))));
}

Mais informações: http://source.coveo.com/2014/10/01/java8-mockito/

GuiSim
fonte
5

As soluções acima realmente não funcionaram no meu caso. Não pude usar o ArgumentCaptor porque o método foi chamado várias vezes e eu precisava validar cada um. Um simples Matcher com "argThat" fez o truque facilmente.

Correspondente personalizado

// custom matcher
private class PolygonMatcher extends ArgumentMatcher<PolygonOptions> {
    private int fillColor;
    public PolygonMatcher(int fillColor) {
        this.fillColor = fillColor;
    }

    @Override
    public boolean matches(Object argument) {
        if (!(argument instanceof PolygonOptions)) return false;
        PolygonOptions arg = (PolygonOptions)argument;
        return Color.red(arg.getFillColor()) == Color.red(fillColor)
                && Color.green(arg.getFillColor()) == Color.green(fillColor)
                && Color.blue(arg.getFillColor()) == Color.blue(fillColor);
    }
}

Test Runner

// do setup work setup
// 3 light green polygons
int green = getContext().getResources().getColor(R.color.dmb_rx_bucket1);
verify(map, times(3)).addPolygon(argThat(new PolygonMatcher(green)));

// 1 medium yellow polygons
int yellow = getContext().getResources().getColor(R.color.dmb_rx_bucket4);
    verify(map, times(1)).addPolygon(argThat(new PolygonMatcher(yellow)));

// 3 red polygons
int orange = getContext().getResources().getColor(R.color.dmb_rx_bucket5);
verify(map, times(3)).addPolygon(argThat(new PolygonMatcher(orange)));

// 2 red polygons
int red = getContext().getResources().getColor(R.color.dmb_rx_bucket7);
verify(map, times(2)).addPolygon(argThat(new PolygonMatcher(red)));
zunir
fonte
3

E solução muito agradável e limpa em koltin de com.nhaarman.mockito_kotlin

verify(mock).execute(argThat {
    this.param = expected
})
Cililing
fonte
1

Você pode consultar o seguinte:

Mockito.verify(mockedObject).someMethodOnMockedObject(eq(desiredObject))

Isso verificará se o método mockedObject é chamado com o desejadoObject como parâmetro.

zaid bepari
fonte
1

Outra maneira fácil de fazer isso:

import org.mockito.BDDMockito;    
import static org.mockito.Matchers.argThat;
import org.mockito.ArgumentMatcher;

BDDMockito.verify(mockedObject)
        .someMethodOnMockedObject(argThat(new ArgumentMatcher<TypeOfMethodArg>() {

            @Override
            public boolean matches(Object argument) {
                final TypeOfMethodArg castedArg = (TypeOfMethodArg) argument;

                // Make your verifications and return a boolean to say if it matches or not
                boolean isArgMarching = true;

                return isArgMarching;
            }
        }));
pierrefevrier
fonte
0

O javadoc para refEq mencionou que a verificação de igualdade é superficial! Você pode encontrar mais detalhes no link abaixo:

[ https://static.javadoc.io/org.mockito/mockito-core/2.2.29/org/mockito/ArgumentMatchers.html#refEq(T,%20java.lang.String...))[1]

O problema "igualdade rasa" não pode ser controlado quando você usa outras classes que não implementam o método .equals (), a classe "DefaultMongoTypeMapper" é um exemplo em que o método .equals () não é implementado.

org.springframework.beans.factory.support oferece um método que pode gerar uma definição de bean em vez de criar uma instância do objeto e pode ser usado para se livrar da Comparison Failure.

 genericBeanDefinition(DefaultMongoTypeMapper.class)
                        .setScope(SCOPE_SINGLETON)
                        .setAutowireMode(AUTOWIRE_CONSTRUCTOR)
                        .setLazyInit(false)
                        .addConstructorArgValue(null)
                        .getBeanDefinition()

** "A definição do bean é apenas uma descrição do bean, não um bean em si. As descrições do bean implementam corretamente equals () e hashCode (), portanto, em vez de criar um novo DefaultMongoTypeMapper (), fornecemos uma definição que informa ao Spring como deve criar um "

No seu exemplo, você pode fazer algo assim

Mockito.verify(mockedObject)
       .doSoething(genericBeanDefinition(YourClass.class).setA("a")
       .getBeanDefinition());
Saif Masadeh
fonte
0

Uma solução simplificada, sem criar uma nova classe de implementação do Matcher e usar a expressão lambda:

        verify(mockObject).someMockMethod(argThat((SomeArgument arg) -> arg.fieldToMatch.equals(expectedFieldValue));
murali mohan
fonte