Mockito สามารถจับข้อโต้แย้งของวิธีที่เรียกหลาย ๆ ครั้งได้หรือไม่?


446

ฉันมีวิธีที่ได้รับการเรียกสองครั้งและฉันต้องการจับอาร์กิวเมนต์ของการเรียกใช้เมธอดที่สอง

นี่คือสิ่งที่ฉันได้ลอง:

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

แต่ฉันได้รับTooManyActualInvocationsข้อยกเว้นเนื่องจาก Mockito คิดว่าdoSomethingควรเรียกเพียงครั้งเดียว

ฉันจะตรวจสอบข้อโต้แย้งของการโทรครั้งที่สองได้doSomethingอย่างไร?

คำตอบ:


784

ฉันคิดว่ามันควรจะเป็น

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

ตัวอย่างจากmockito javadoc :

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()แต่ละการร้องขอด้วยสิ่งนี้ได้หรือไม่?
matt b

36
ควรสังเกตว่าในกรณีที่คุณทำสิ่งนี้: Person person = new Person("John"); doSomething(person); person.setName("Jane"); doSomething(person);อาร์กิวเมนต์ที่จับจะเหมือนเดิมสองครั้ง (เพราะจริงๆแล้วมันเป็นวัตถุบุคคลเดียวกัน) ดังนั้นcapturedPeople.get(0).getName() == capturedPeople.get(1).getName() == "Jane"โปรดดูที่groups.google.com/forum/#!msg/mockito/ KBRocVedYT0
asmaier

2
นี่เป็นสิ่งที่ดี แต่ฉันจะทดสอบการเรียกใช้วัตถุที่แตกต่างกันสองชนิดได้อย่างไร ตัวอย่างเช่น ExecutorService.submit (ใหม่ MyRunableImpl ()); แล้ว ExecutorService.submit (ใหม่ MyAnotherRunableImpl ())?
Leon

หากต้องการจัดการกรณีที่อธิบายโดย @asmaier ฉันโพสต์คำตอบที่นี่: stackoverflow.com/a/36574817/1466267
SpaceTrucker

1
สำหรับทุกคนที่ยังสงสัยเกี่ยวกับคำตอบสำหรับคำถามของลีออนคุณจะต้องใช้คลาสพื้นฐานทั่วไป ( Runnable) และหากจำเป็นให้ทำการตรวจสอบประเภทเฉพาะเจาะจงมากขึ้นในการโต้แย้งที่ถูกจับ
Matthew อ่าน

50

ตั้งแต่ Mockito 2.0 นอกจากนี้ยังมีความเป็นไปได้ที่จะใช้วิธีแบบคงที่Matchers.argThat (ArgumentMatcher) ด้วยความช่วยเหลือของ Java 8 ทำให้สามารถเขียนและอ่านได้ง่ายขึ้น:

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

หากคุณเชื่อมโยงกับเวอร์ชั่น Java ที่ต่ำกว่าก็มีสิ่งที่ไม่ดีเช่นกัน:

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

แน่นอนว่าไม่มีสิ่งใดที่สามารถตรวจสอบลำดับการโทรได้ซึ่งคุณควรใช้InOrder :

InOrder inOrder = inOrder(mockBar);

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

โปรดดูที่โครงการmockito-java8ซึ่งทำให้สามารถโทรออกได้เช่น:

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

2
นี่เป็นเทคนิคที่ดี ขณะนี้ฉันได้รับผลลัพธ์ที่ค่อนข้างเป็นความลับ แต่: "ต้องการ แต่ไม่ได้เรียกใช้: / n mockAppender.append (<ตัวจัดการดัชนีใช้ $$ lambda $ 5 9/1 3 1 9 5 1 0 1 6>)"; - CharSequenceหาเรื่องมี คุณรู้วิธีการรับรายงานเพื่อพิมพ์หาเรื่อง "ต้องการ" อย่างถูกต้องหรือไม่?
mike rodent

@mikerodent เอาต์พุตที่เข้ารหัสลับสามารถแก้ไขได้หากคุณไปเส้นทางที่ละเอียดมากขึ้นในการสร้างคลาสที่ใช้ ArgumentMatcher <T> การแทนที่เมธอด toString ในการนำไปใช้ของคุณจะให้ข้อความใด ๆ ที่คุณต้องการในผลลัพธ์การทดสอบ mockito
โนอาห์โซโลมอน

25

หากคุณไม่ต้องการที่จะตรวจสอบทุกสายไปเพียงคนสุดท้ายคุณก็สามารถใช้doSomething() ArgumentCaptor.getValue()ตามMockito javadoc :

หากวิธีการนั้นถูกเรียกหลายครั้งก็จะส่งกลับค่าที่บันทึกล่าสุด

ดังนั้นจะใช้งานได้ (สมมติว่าFooมีวิธีการ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());

มีวิธีใดในการจับค่าทั้งสอง?
Hars

9

คุณยังสามารถใช้ @Captor หมายเหตุประกอบ ArgumentCaptor ตัวอย่างเช่น:

@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

ด้วย lambdas ของ Java 8 วิธีที่สะดวกคือการใช้

org.mockito.invocation.InvocationOnMock

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

ฉันไม่สามารถดูว่ามันสะดวกกว่าแบบเดิมอย่างไร ฉันชอบใช้ lambdas ดี แต่ฉันไม่แน่ใจว่าเป็นอย่างนี้หรือไม่
Eric Wilson

0

ก่อนอื่น: คุณควรนำเข้า mockito คงที่วิธีนี้โค้ดจะสามารถอ่านได้มากขึ้น (และใช้งานง่าย) - ตัวอย่างโค้ดด้านล่างนี้ต้องการให้มันทำงาน:

import static org.mockito.Mockito.*;

ในเมธอด Verify () คุณสามารถผ่าน ArgumentCaptor เพื่อรับรองการดำเนินการในการทดสอบและ ArgumentCaptor เพื่อประเมินข้อโต้แย้ง:

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

รายการอาร์กิวเมนต์ที่ส่งผ่านทั้งหมดในระหว่างการทดสอบของคุณสามารถเข้าถึงได้ผ่านเมธอด argument.getAllValues ​​()

อาร์กิวเมนต์ของค่า (เรียกครั้งสุดท้าย) สามารถเข้าถึงได้ผ่านทาง argument.getValue () สำหรับการจัดการ / ตรวจสอบเพิ่มเติมหรือสิ่งที่คุณต้องการจะทำเพิ่มเติม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.