Verifikasi nilai atribut objek dengan mockito

264

Saya memiliki panggilan metode yang ingin saya tiru dengan mockito. Untuk mulai dengan, saya telah membuat dan menyuntikkan instance objek di mana metode akan dipanggil. Tujuan saya adalah memverifikasi salah satu objek dalam pemanggilan metode.

Apakah ada cara yang memungkinkan mockito Anda untuk menegaskan atau memverifikasi objek dan atributnya ketika metode mock dipanggil?

contoh

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

Alih-alih melakukan anyObject()saya ingin memeriksa bahwa objek argumen berisi beberapa bidang tertentu

Mockito.verify(mockedObject)
       .someMethodOnMockedObject(
              Mockito.<SomeObjectAsArgument>**compareWithThisObject()**)
Priyank
sumber
Sebagai alternatif untuk menggunakan mockito dalam kasus ini, Anda dapat mempertimbangkan untuk membuat rintisan kustom yang memperluas kelas mockedObject, dan menimpa beberapaMethodOnMockedObject untuk menyimpan objek untuk perbandingan nanti.
Gonen I

Jawaban:

540

Fitur baru yang ditambahkan ke Mockito menjadikan ini lebih mudah,

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

Lihatlah dokumentasi Mockito


Jika ada lebih dari satu parameter, dan hanya menangkap satu parameter yang diinginkan, gunakan ArgumentMatchers lain untuk membungkus seluruh argumen:

verify(mock).doSomething(eq(someValue), eq(someOtherValue), argument.capture());
assertEquals("John", argument.getValue().getName());
iraSenthil
sumber
1
jika metode Anda memiliki lebih dari satu argumen, Anda juga harus menggunakan Pencocokan untuk semua argumen lainnya. akcasoy.wordpress.com/tag/argumentcaptor
robsonrosa
1
Bagaimana jika ada banyak argumen? Bagaimana Anda menentukan yang tepat yang Anda minati?
IgorGanapolsky
2
@IgorGanapolsky Mengasumsikan parameter String kedua untuk doSomething yang perlu Anda lakukan: verifikasi (mock) .doSomething (argumen.capture (), anyString ());
GreenTurtle
kebutuhan untuk menggunakan pencocokan untuk semua argumen semata-mata sesuai dengan spesifikasi penggunaan pencocokan semua atau tidak sama sekali.
Charney Kaye
54

Saya pikir cara termudah untuk memverifikasi objek argumen adalah dengan menggunakan refEqmetode ini:

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

Itu dapat digunakan bahkan jika objek tidak mengimplementasikan equals(), karena refleksi digunakan. Jika Anda tidak ingin membandingkan beberapa bidang, cukup tambahkan namanya sebagai argumen untuk refEq.

John29
sumber
1
itu cara yang sangat elegan tapi sayangnya org.mockito.Matchers sekarang sudah tidak digunakan lagi
ihebiheb
5
@ihebiheb Sudah dipindahkan ke ArgumentMatchers
Michael
48

Satu lagi kemungkinan, jika Anda tidak ingin menggunakan ArgumentCaptor(misalnya, karena Anda juga menggunakan stubbing), adalah menggunakan Pencocokan Hamcrest bersama dengan Mockito.

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

Mockito.verify(mockedObject).someMethodOnMockedObject(MockitoHamcrest.argThat(
    Matchers.<SomeObjectAsArgument>hasProperty("propertyName", desiredValue)));
charleyc
sumber
2
Sidenote: pastikan Matcherspaketnya benar, karena menulis baris kode yang sama dengan org.mockito.Matcherskelas melemparkan pengecualian yang menyesatkan yang menyatakan bahwa parameter fungsi mock tidak cocok.
buer
1
Harap dicatat bahwa dalam versi Mockito modern, ini MockitoHamcrest.argThat()dan tidakMockito.argThat()
Roman Puchkovskiy
17

Ini adalah jawaban berdasarkan jawaban dari iraSenthil tetapi dengan anotasi ( Penculik ). Menurut saya itu memiliki beberapa keunggulan:

  • lebih pendek
  • lebih mudah dibaca
  • dapat menangani obat generik tanpa peringatan

Contoh:

@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
sumber
Ini hanya akan berfungsi untuk satu argumen dalam params.
IgorGanapolsky
Anda dapat menggunakan satu penculik untuk lebih dari satu argumen. Jika Anda menangkap lebih dari satu argumen, Anda dapat mendaftar semua hasil captor.getAllValues(). Metode captor.getValue()yang digunakan dalam jawaban memberikan hasil terakhir.
Walery Strauch
11

Jika Anda menggunakan Java 8, Anda dapat menggunakan ekspresi Lambda untuk mencocokkan.

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

Contoh panggilan

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

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

Info lebih lanjut: http://source.coveo.com/2014/10/01/java8-mockito/

GuiSim
sumber
5

Solusi di atas tidak benar-benar berfungsi dalam kasus saya. Saya tidak bisa menggunakan ArgumentCaptor karena metode ini dipanggil beberapa kali dan saya perlu memvalidasi masing-masing. Pencocokan sederhana dengan "argThat" melakukan trik dengan mudah.

Pencocokan Kustom

// 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)));
mendesis
sumber
3

Dan solusi yang sangat bagus dan bersih di koltin dari com.nhaarman.mockito_kotlin

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

Anda dapat merujuk yang berikut ini:

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

Ini akan memverifikasi apakah metode mockedObject dipanggil dengan parameter yang diinginkan.

zaid bepari
sumber
1

Cara mudah lain untuk melakukannya:

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
sumber
0

Javadoc untuk refEq menyebutkan bahwa pemeriksaan kesetaraan adalah dangkal! Anda dapat menemukan detail lebih lanjut di tautan di bawah ini:

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

Masalah "shallow equality" tidak dapat dikontrol ketika Anda menggunakan kelas lain yang tidak menerapkan metode .equals (), kelas "DefaultMongoTypeMapper" adalah contoh di mana metode .equals () tidak diterapkan.

org.springframework.beans.factory.support menawarkan metode yang dapat menghasilkan definisi kacang alih-alih membuat turunan objek, dan dapat digunakan untuk menghilangkan Kegagalan Perbandingan.

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

** "Definisi bean hanya merupakan deskripsi dari bean, bukan bean itu sendiri. Deskripsi bean mengimplementasikan dengan benar equals () dan hashCode (), jadi daripada membuat DefaultMongoTypeMapper baru (), kami memberikan definisi yang memberitahu bagaimana itu muncul. harus membuat satu "

Dalam contoh Anda, Anda dapat melakukan sesuatu seperti ini

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

Solusi yang disederhanakan, tanpa membuat kelas implementasi Matcher baru dan menggunakan ekspresi lambda:

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