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()**)

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

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 .


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

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

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/


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

3

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

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

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.


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

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

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));
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.