วิธีตรวจสอบการเรียกหลายวิธีด้วยพารามิเตอร์ที่แตกต่างกัน


116

ฉันมีวิธีการต่อไปนี้ที่ต้องการตรวจสอบพฤติกรรม

public void methodToTest(Exception e, ActionErrors errors) {
    ...

    errors.add("exception.message", 
            ActionMessageFactory.createErrorMessage(e.toString()));

    errors.add("exception.detail",
            ActionMessageFactory.createErrorMessage(e.getStackTrace()[0].toString()));

    ...
}

ในชั้นเรียน @Test ของฉันฉันหวังว่าจะทำอะไรแบบนี้เพื่อยืนยันว่าerrors.add()ถูกเรียกด้วย " except.message " และอีกครั้งด้วย "except.detail"

verify(errors).add(eq("exception.message"), any(ActionError.class));
verify(errors).add(eq("exception.detail"), any(ActionError.class));

อย่างไรก็ตาม Mockito บ่นดังนี้

Argument(s) are different! Wanted:
actionErrors.add(
    "exception.message",
    <any>
);

Actual invocation has different arguments:
actionErrors.add(
    "exception.detail",
    org.apache.struts.action.ActionError@38063806
);

ฉันจะบอกให้ Mockito ตรวจสอบทั้งสองค่าได้อย่างไร


1
เมื่อคุณมี 2 วิธีที่มีลายเซ็นที่แตกต่างกันคุณสามารถเขียนกรณีทดสอบแยกกันสำหรับทั้งสองอย่าง
Naveen Babu

8
ใช่ แต่ในกรณีนี้เป็นลายเซ็นวิธีเดียวกัน แต่มีค่าอาร์กิวเมนต์ต่างกัน
แบรด

คุณสามารถลองใช้Mockito.reset()
takacsot

คำตอบ:


103

การอ่านเพิ่มเติมทำให้ฉันลองใช้ ArgumentCaptors และผลงานต่อไปนี้แม้ว่าจะมีรายละเอียดมากกว่าที่ฉันต้องการก็ตาม

ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);

verify(errors, atLeastOnce()).add(argument.capture(), any(ActionMessage.class));

List<String> values = argument.getAllValues();

assertTrue(values.contains("exception.message"));
assertTrue(values.contains("exception.detail"));

มีวิธีตรวจสอบให้แน่ใจว่ามีการจับคู่พารามิเตอร์บางตัวโดยใช้แนวทางนี้หรือไม่? พูดเช่นวิธีการ OP ที่มีสองข้อโต้แย้งและต้องการที่จะตรวจสอบพวกเขาถูกเรียกกัน
committedandroider

1
กรณีทดสอบของ OP เรียกmethodToTest()เพียงครั้งเดียวดังนั้นคำตอบนี้จะตรวจสอบว่ามีการโทรทั้งสองพร้อมกัน การตรวจจับList<String> valuesที่ถูกยืนยันจะมีเพียงสองค่าที่กำลังทดสอบและไม่มีค่าอื่น ๆ assertTrue(values.size == 2)นอกจากนี้คุณยังสามารถเพิ่ม หากนี่คือสิ่งที่คุณต้องการฉันจะแทนที่ 3 assertTrue statement ด้วยHamcrestเดียว...assertThat(values, contains("exception.message", "exception.detail"));
Brad

ไม่ OP กรณีการเรียกใช้ methodToTest () สองครั้ง?
committedandroider

ขอโทษฉันไม่ชัดเจน ฉันอ้างถึงสถานการณ์ที่ OP ต้องการทดสอบว่ามีการเรียกอาร์กิวเมนต์สองตัวร่วมกัน ดังนั้นลายเซ็นเมธอดจะมีลักษณะเป็นโมฆะสาธารณะ methodToTest (ข้อยกเว้น e, ข้อความ m, ข้อผิดพลาด ActionErrors) {เพื่อให้มีการเรียกข้อยกเว้นเฉพาะพร้อมข้อความเฉพาะ ผมคิดว่าคุณก็สามารถมีสอง ArgumentCaptors แล้วดึงดัชนีและเปรียบเทียบโดยใช้ค่าดัชนีที่เหล่านั้นทั้งในรายการค่า
committedandroider

กรณีทดสอบของ OP โทรmethodToTest()ครั้งเดียว เป็นอาร์กิวเมนต์เมธอดActionErrors errorsถูกเรียกใช้ภายในสองครั้ง
Brad

61

หากลำดับของการadd()โทรทั้งสองเกี่ยวข้องกันคุณสามารถใช้InOrder:

InOrder inOrder = inOrder(errors);
inOrder.verify(errors).add(eq("exception.message"), any(ActionError.class));
inOrder.verify(errors).add(eq("exception.detail"), any(ActionError.class));

7
มันเพียงพอที่จะส่งผ่านerrorsอาร์กิวเมนต์เดียว: InOrder inOrder = inOrder(errors);(ดูเอกสาร )
GreenhouseVeg

2
จะเกิดอะไรขึ้นหากคำสั่งซื้อไม่เกี่ยวข้อง ซึ่งมักจะเป็นเช่นนั้น
haelix

1
@haelix ในกรณีนั้นให้ใช้คำตอบของ Brads แปลงListไปSetและยืนยันว่าการตั้งค่าของปัจจัยการผลิตเท่ากับชุดที่กำหนดโดยการจับข้อโต้แย้ง
flopshot

25

ลองทำสิ่งนี้:

verify(errors, times(2))
     .add(AdditionalMatchers.or(eq("exception.message"), eq("exception.detail")),
          any(ActionError.class));

6
เห็นได้ชัดว่าเช็คของคุณผ่อนคลายเกินไป
haelix

17

คุณอาจมีปัญหาในรหัสของคุณ เพราะตามความเป็นจริงคุณเขียนโค้ดนี้:

Map<Character, String> map = mock(Map.class);

map.put('a', "a");
map.put('b', "b");
map.put('c', "c");

verify(map).put(eq('c'), anyString());
verify(map).put(eq('a'), anyString());
verify(map).put(eq('b'), anyString());

โปรดทราบว่าการตรวจสอบครั้งแรกไม่ได้เป็นไปตามลำดับในการเรียกร้องจริง

นอกจากนี้ฉันขอแนะนำให้คุณอย่าล้อเลียนประเภทที่คุณไม่ได้เป็นเจ้าของเช่นประเภทเสา

[แก้ไข @Brad]

หลังจากเรียกใช้โค้ดของ Brice (ด้านบน) ใน IDE ของฉันฉันเห็นว่าฉันใช้ ActionError แทน ActionMessage นั่นคือสาเหตุที่การยืนยัน () ของฉันไม่ตรงกัน ข้อความแสดงข้อผิดพลาดที่ฉันโพสต์ในตอนแรกทำให้ฉันเข้าใจผิดคิดว่าเป็นอาร์กิวเมนต์แรกที่ไม่ตรงกัน ปรากฎว่ามันเป็นการโต้เถียงครั้งที่สอง

ดังนั้นคำตอบสำหรับคำถามของฉันคือ

/** 
 * note that ActionMessageFactory.createErrorMessage() returns ActionMessage
 * and ActionError extends ActionMessage
 */
verify(errors).add(eq("exception.message"), any(ActionMessage.class));
verify(errors).add(eq("exception.detail"), any(ActionMessage.class));

1
อย่าเข้าใจในสิ่งที่คุณพยายามจะพูด ลำดับการตรวจสอบมีความสำคัญหรือไม่? หากคำสั่งตรวจสอบเป็นเรื่องสำคัญ เหตุใดจึงมี InOrder api ให้มา
Oleksandr Papchenko

เช่นเดียวกับสิ่งที่เขียนไว้ข้างต้นคำสั่งการตรวจสอบไม่เกี่ยวข้อง InOrderว่าทำไมมี
Brice

12

คุณสามารถใช้Mockito.atLeastOnce()ซึ่งทำให้ Mockito ผ่านการทดสอบได้แม้ว่าจะมีการเรียกม็อกบิทนั้นหลายครั้งก็ตาม

Mockito.verify(mockObject, Mockito.atLeastOnce()).testMethod(Mockito.eq(1));

Mockito.verify(mockObject, Mockito.atLeastOnce()).testMethod(Mockito.eq(2));

1

1) บอก Mokito ถึงความคาดหวังในการโทรทั้งหมด

2) บอก Mokito ถึงจำนวนครั้งที่คาดว่าจะใช้ชุดค่าผสมแต่ละพารามิเตอร์

verify(errors, times(2)).add(any(), any(ActionMessage.class));

verify(errors, atLeastOnce()).add(eq("exception.message"), any());
verify(errors, atLeastOnce()).add(eq("exception.detail"), any());

0

ในทำนองเดียวกันกับ @ sendon1928 เราสามารถใช้:

Mockito.times(wantedInvocationCount)

เพื่อให้แน่ใจว่าวิธีการนี้ถูกเรียกว่าจำนวนครั้งที่แน่นอน (วิธีที่ดีกว่าในความคิดของฉัน) หลังจากนั้นเราสามารถโทร

Mockito.verifyNoMoreInteractions(mock)

เพื่อให้แน่ใจว่าไม่ได้ใช้การเยาะเย้ยเพิ่มเติมในบริบทใด ๆ ตัวอย่างเต็ม:

Mockito.verify(mockObject, Mockito.times(wantedInvocationCount)).testMethod(Mockito.eq(1));

Mockito.verify(mockObject, Mockito.times(wantedInvocationCount)).testMethod(Mockito.eq(2));

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