Mockito, birden çok kez çağrılan bir yöntemin argümanlarını yakalayabilir mi?


446

İki kez çağrılan bir yöntem var ve ikinci yöntem çağrısının argümanını yakalamak istiyorum.

İşte denedim:

ArgumentCaptor<Foo> firstFooCaptor = ArgumentCaptor.forClass(Foo.class);
ArgumentCaptor<Foo> secondFooCaptor = ArgumentCaptor.forClass(Foo.class);
verify(mockBar).doSomething(firstFooCaptor.capture());
verify(mockBar).doSomething(secondFooCaptor.capture());
// then do some assertions on secondFooCaptor.getValue()

Ama TooManyActualInvocationsMockito'nun düşündüğü gibi bir İstisna alıyorumdoSomething sadece bir kez çağrılması gerektiğini .

İkinci çağrısının argümanını nasıl doğrulayabilirim doSomething?

Yanıtlar:


784

Bence olmalı

verify(mockBar, times(2)).doSomething(...)

Mockito javadoc'tan örnek :

ArgumentCaptor<Person> peopleCaptor = ArgumentCaptor.forClass(Person.class);
verify(mock, times(2)).doSomething(peopleCaptor.capture());

List<Person> capturedPeople = peopleCaptor.getAllValues();
assertEquals("John", capturedPeople.get(0).getName());
assertEquals("Jane", capturedPeople.get(1).getName());

3
doSomething()Bununla ilgili her ayrı çağrıda aktarılan argümanları yakalayabilir misiniz ?
matt b

36
Böyle bir şey yapmanız durumunda: Person person = new Person("John"); doSomething(person); person.setName("Jane"); doSomething(person);yakalanan argümanın iki kez aynı olacağı belirtilmelidir (çünkü aslında aynı kişi nesnesidir), bu yüzden capturedPeople.get(0).getName() == capturedPeople.get(1).getName() == "Jane"ayrıca groups.google.com/forum/#!msg/mockito/ adresine bakın. KBRocVedYT0 / 5HtARMl9r2wJ .
asmaier

2
Bu güzel, ama iki farklı tipte nesne çağrısını nasıl test edebilirim? Örneğin ExecutorService.submit (yeni MyRunableImpl ()); ve sonra ExecutorService.submit (yeni MyAnotherRunableImpl ())?
Leon

Bir kişinin @asmaier tarafından açıklanan davayı ele alması gerekiyorsa, burada bir cevap gönderdim: stackoverflow.com/a/36574817/1466267
SpaceTrucker

1
Leon'un sorusunun cevabını hala merak eden herkes için ortak taban sınıfını ( Runnable) kullanır ve gerekirse yakalanan argümanda daha spesifik bir tür kontrolü yaparsınız.
Matthew

50

Mockito 2.0'dan bu yana, Matchers.argThat (ArgumentMatcher) statik yöntemini kullanma olasılığı da vardır . Java 8'in yardımıyla artık yazmak daha temiz ve daha okunaklı:

verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("OneSurname")));
verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("AnotherSurname")));

Daha düşük Java sürümüne bağlıysanız, o kadar da kötü değil:

verify(mockBar).doSth(argThat(new ArgumentMatcher<Employee>() {
        @Override
        public boolean matches(Object emp) {
            return ((Employee) emp).getSurname().equals("SomeSurname");
        }
    }));

Elbette bunların hiçbiri InOrder'ı kullanmanız gereken arama sırasını doğrulayamaz :

InOrder inOrder = inOrder(mockBar);

inOrder.verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("FirstSurname")));
inOrder.verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("SecondSurname")));

Lütfen aşağıdaki gibi arama yapmayı mümkün kılan mockito-java8 projesine göz atın :

verify(mockBar).doSth(assertArg(arg -> assertThat(arg.getSurname()).isEqualTo("Surname")));

2
Bu güzel bir teknik. Şu anda bazı oldukça şifreli çıktılar alıyorum: "Aranıyor ama çağrılmadı: / n mockAppender.append (<Dizin yöneticisi ut $$ lambda $ 5 9/1 3 1 9 5 1 0 1 6>);" - arg bir var CharSequence. Raporun "aranıyor" argümanını düzgün şekilde yazdırmasını sağlamanın herhangi bir yolunu biliyor musunuz?
mike rodent

@mikerodent ArgumentMatcher <T> uygulayan bir sınıf oluşturmanın daha ayrıntılı yoluna giderseniz şifreli çıktı düzeltilebilir. Uygulamanızda toString yönteminin geçersiz kılınması, mockito test çıktısında istediğiniz iletileri sağlayacaktır.
Noah Solomon

25

doSomething()Sadece sonuncusu için yapılan tüm aramaları doğrulamak istemiyorsanız kullanabilirsiniz ArgumentCaptor.getValue(). Mockito javadoc'a göre :

Yöntem birden çok kez çağrıldıysa, yakalanan en son değeri döndürür

Yani bu işe yarayacaktır ( Foobir yöntemi olduğunu varsayar getName()):

ArgumentCaptor<Foo> fooCaptor = ArgumentCaptor.forClass(Foo.class);
verify(mockBar, times(2)).doSomething(fooCaptor.capture());
//getValue() contains value set in second call to doSomething()
assertEquals("2nd one", fooCaptor.getValue().getName());

her iki değeri de yakalamanın bir yolu var mı?
Hars

9

@Captor açıklamalı ArgumentCaptor öğesini de kullanabilirsiniz. Örneğin:

@Mock
List<String> mockedList;

@Captor
ArgumentCaptor<String> argCaptor;

@BeforeTest
public void init() {
    //Initialize objects annotated with @Mock, @Captor and @Spy.
    MockitoAnnotations.initMocks(this);
}

@Test
public void shouldCallAddMethodTwice() {
    mockedList.add("one");
    mockedList.add("two");
    Mockito.verify(mockedList, times(2)).add(argCaptor.capture());

    assertEquals("one", argCaptor.getAllValues().get(0));
    assertEquals("two", argCaptor.getAllValues().get(1));
}

6

Java 8'in lambdasları ile,

org.mockito.invocation.InvocationOnMock

when(client.deleteByQuery(anyString(), anyString())).then(invocationOnMock -> {
    assertEquals("myCollection", invocationOnMock.getArgument(0));
    assertThat(invocationOnMock.getArgument(1), Matchers.startsWith("id:"));
}

Bunun eski yöntemden nasıl daha uygun olduğunu göremiyorum. Lambdaların iyi kullanımını seviyorum, ama bunun bir olup olmadığından emin değilim.
Eric Wilson

0

Her şeyden önce: her zaman mockito static'i içe aktarmalısınız, bu şekilde kod çok daha okunabilir (ve sezgisel) olacaktır - aşağıdaki kod örnekleri çalışmasını gerektirir:

import static org.mockito.Mockito.*;

Verify () yönteminde, testte yürütmeyi sağlamak için ArgumentCaptor ve argümanları değerlendirmek için ArgumentCaptor iletebilirsiniz:

ArgumentCaptor<MyExampleClass> argument = ArgumentCaptor.forClass(MyExampleClass.class);
verify(yourmock, atleast(2)).myMethod(argument.capture());

List<MyExampleClass> passedArguments = argument.getAllValues();

for (MyExampleClass data : passedArguments){
    //assertSometing ...
    System.out.println(data.getFoo());
}

Testiniz sırasında iletilen tüm bağımsız değişkenlerin listesine argument.getAllValues ​​() yöntemi aracılığıyla erişilebilir.

Tek (son çağrılan) bağımsız değişkenin değerine, daha fazla manipülasyon / kontrol veya ne yapmak isterseniz yapmak için argument.getValue () aracılığıyla erişilebilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.