matchers Mockitoวิธีการแบบคงที่และโทรไปยังวิธีการเหล่านั้นซึ่งยืนอยู่ในข้อโต้แย้งในช่วงสายไปและwhen
verify
Hamcrest matchers (เวอร์ชันที่เก็บถาวร) (หรือตัวจับคู่แบบ Hamcrest) เป็นอินสแตนซ์อ็อบเจ็กต์ที่ไม่มีสถานะและมีวัตถุประสงค์ทั่วไปที่ใช้Matcher<T>
และแสดงวิธีการmatches(T)
ที่ส่งคืนจริงหากอ็อบเจ็กต์ตรงกับเกณฑ์ของ Matcher มีวัตถุประสงค์เพื่อให้ปราศจากผลข้างเคียงและโดยทั่วไปจะใช้ในการยืนยันเช่นด้านล่าง
/* Mockito */ verify(foo).setPowerLevel(gt(9000));
/* Hamcrest */ assertThat(foo.getPowerLevel(), is(greaterThan(9000)));
Mockito matchers มีอยู่แยกต่างหากจากตัวจับคู่แบบ Hamcrest เพื่อให้คำอธิบายของนิพจน์ที่ตรงกันพอดีกับการเรียกใช้เมธอดโดยตรง : Mockito matchers จะส่งคืนT
โดยที่วิธีการจับคู่ Hamcrest จะส่งคืนอ็อบเจ็กต์ Matcher (ประเภทMatcher<T>
)
matchers Mockito ถูกเรียกด้วยวิธีการแบบคงที่เช่นeq
, any
, gt
และstartsWith
บนและorg.mockito.Matchers
org.mockito.AdditionalMatchers
นอกจากนี้ยังมีอะแดปเตอร์ซึ่งมีการเปลี่ยนแปลงในรุ่น Mockito:
- สำหรับ Mockito 1.x การ
Matchers
เรียกใช้บางสาย (เช่นintThat
หรือargThat
) คือ Mockito matchers ที่ยอมรับ Hamcrest matchers เป็นพารามิเตอร์โดยตรง ArgumentMatcher<T>
ขยายorg.hamcrest.Matcher<T>
ซึ่งใช้ในการเป็นตัวแทน Hamcrest ภายในและเป็นคลาสพื้นฐานของ Hamcrest matcherแทนที่จะเป็นMockito matcher ประเภทใด ๆ
- สำหรับ Mockito 2.0+ Mockito ไม่มีการพึ่งพาโดยตรงกับ Hamcrest อีกต่อไป
Matchers
เรียกวลีเป็นintThat
หรือargThat
ห่อArgumentMatcher<T>
วัตถุที่ไม่ได้ใช้งานอีกต่อไปorg.hamcrest.Matcher<T>
แต่ใช้ในลักษณะที่คล้ายกัน อะแดปเตอร์ Hamcrest เช่นargThat
และintThat
ยังคงมีอยู่ แต่ได้ย้ายไปที่MockitoHamcrest
แทน
ไม่ว่าผู้จับคู่จะเป็นแฮมเครสต์หรือสไตล์แฮมเครสต์ก็สามารถปรับเปลี่ยนได้ดังนี้:
/* Mockito matcher intThat adapting Hamcrest-style matcher is(greaterThan(...)) */
verify(foo).setPowerLevel(intThat(is(greaterThan(9000))));
ในข้อความข้างต้น: foo.setPowerLevel
เป็นวิธีการที่ยอมรับint
ไฟล์. is(greaterThan(9000))
ส่งคืน a Matcher<Integer>
ซึ่งไม่สามารถใช้เป็นsetPowerLevel
อาร์กิวเมนต์ได้ ตัวจับคู่ Mockito intThat
ห่อ Matcher แบบ Hamcrest และส่งคืนint
เพื่อให้สามารถปรากฏเป็นอาร์กิวเมนต์ได้ ตัวจับคู่ Mockito ต้องการgt(9000)
รวมนิพจน์ทั้งหมดไว้ในการเรียกเพียงครั้งเดียวดังเช่นในบรรทัดแรกของโค้ดตัวอย่าง
ผู้จับคู่ทำอะไร / ส่งคืน
when(foo.quux(3, 5)).thenReturn(true);
เมื่อไม่ใช้ตัวจับคู่อาร์กิวเมนต์ Mockito จะบันทึกค่าอาร์กิวเมนต์ของคุณและเปรียบเทียบกับequals
วิธีการของพวกเขา
when(foo.quux(eq(3), eq(5))).thenReturn(true); // same as above
when(foo.quux(anyInt(), gt(5))).thenReturn(true); // this one's different
เมื่อคุณเรียกตัวจับคู่เช่นany
หรือgt
(มากกว่า) Mockito จะจัดเก็บวัตถุที่จับคู่ซึ่งทำให้ Mockito ข้ามการตรวจสอบความเท่าเทียมกันนั้นและใช้การจับคู่ที่คุณเลือก ในกรณีที่จัดargumentCaptor.capture()
เก็บตัวจับคู่ที่บันทึกอาร์กิวเมนต์แทนเพื่อการตรวจสอบในภายหลัง
matchers กลับค่าหุ่นnull
ดังกล่าวเป็นศูนย์คอลเลกชันที่ว่างเปล่าหรือ Mockito พยายามที่จะกลับมาเป็นที่ปลอดภัยค่าหุ่นที่เหมาะสมเช่น 0 anyInt()
หรือany(Integer.class)
หรือที่ว่างเปล่าสำหรับList<String>
anyListOf(String.class)
เนื่องจากการลบประเภท Mockito จึงขาดข้อมูลประเภทที่จะส่งคืนค่าใด ๆ แต่null
สำหรับany()
หรือargThat(...)
ซึ่งอาจทำให้เกิด NullPointerException ได้หากพยายาม "unbox อัตโนมัติ" เป็นnull
ค่าดั้งเดิม
Matchers ชอบeq
และgt
รับค่าพารามิเตอร์ ตามหลักการแล้วควรคำนวณค่าเหล่านี้ก่อนที่การตัด / การตรวจสอบจะเริ่มขึ้น การโทรเยาะเย้ยในระหว่างการล้อเลียนการโทรสายอื่นอาจรบกวนการสะดุดได้
ไม่สามารถใช้เมธอด Matcher เป็นค่าส่งคืน ไม่มีทางที่จะใช้วลีthenReturn(anyInt())
หรือthenReturn(any(Foo.class))
ใน Mockito ได้เช่น Mockito จำเป็นต้องทราบอย่างชัดเจนว่าอินสแตนซ์ใดที่จะส่งคืนในการโทรที่ถูกขีดฆ่าและจะไม่เลือกค่าตอบแทนโดยพลการให้คุณ
รายละเอียดการดำเนินการ
matchers จะถูกเก็บไว้ (เป็น matchers วัตถุ Hamcrest สไตล์) ในสแต็คที่มีอยู่ในระดับที่เรียกว่าArgumentMatcherStorage MockitoCore และ Matchers ต่างเป็นเจ้าของอินสแตนซ์ThreadSafeMockingProgressซึ่งมีอินสแตนซ์ ThreadLocal ที่ถือ MockingProgress แบบคงที่ มันนี้MockingProgressImplที่ถือคอนกรีตArgumentMatcherStorageImpl ดังนั้นสถานะจำลองและการจับคู่จึงเป็นแบบคงที่ แต่มีการกำหนดขอบเขตเธรดอย่างสม่ำเสมอระหว่างคลาส Mockito และ Matchers
ส่วนใหญ่โทรจับคู่เพียง แต่เพิ่มไปยังกองนี้มีข้อยกเว้นสำหรับ matchers เหมือนand
,or
not
และ สิ่งนี้สอดคล้องอย่างสมบูรณ์กับ (และอาศัย) ลำดับการประเมินของ Javaซึ่งประเมินอาร์กิวเมนต์จากซ้ายไปขวาก่อนที่จะเรียกใช้เมธอด:
when(foo.quux(anyInt(), and(gt(10), lt(20)))).thenReturn(true);
[6] [5] [1] [4] [2] [3]
นี่จะ:
- เพิ่ม
anyInt()
ลงในสแต็ก
- เพิ่ม
gt(10)
ลงในสแต็ก
- เพิ่ม
lt(20)
ลงในสแต็ก
- ลบ
gt(10)
และและเพิ่มlt(20)
and(gt(10), lt(20))
- โทร
foo.quux(0, 0)
ซึ่ง (ยกเว้นกรณีที่ค้างอยู่เป็นอย่างอื่น) false
ผลตอบแทนที่คุ้มค่าเริ่มต้น Mockito ภายในทำเครื่องหมายquux(int, int)
ว่าเป็นการโทรล่าสุด
- การโทร
when(false)
ซึ่งจะละทิ้งอาร์กิวเมนต์และเตรียมที่จะใช้วิธีการต้นขั้วที่quux(int, int)
ระบุใน 5 สถานะที่ถูกต้องมีเพียงสองสถานะเท่านั้นที่มีความยาวสแต็ก 0 (ความเท่าเทียมกัน) หรือ 2 (ตัวจับคู่) และมีตัวจับคู่สองตัวบนสแต็ก (ขั้นตอนที่ 1 และ 4) ดังนั้น Mockito จะตัดเมธอดด้วยตัวany()
จับคู่สำหรับอาร์กิวเมนต์แรกและand(gt(10), lt(20))
สำหรับอาร์กิวเมนต์ที่สองและล้างสแต็ก
นี่แสดงให้เห็นกฎบางประการ:
Mockito ไม่สามารถบอกความแตกต่างระหว่างและquux(anyInt(), 0)
quux(0, anyInt())
ทั้งคู่ดูเหมือนโทรquux(0, 0)
คุยกับ int matcher หนึ่งคนในสแต็ก ดังนั้นหากคุณใช้ตัวจับคู่หนึ่งตัวคุณจะต้องจับคู่อาร์กิวเมนต์ทั้งหมด
เพื่อโทรไม่สำคัญเพียง แต่สิ่งที่ทำให้การทำงานทั้งหมด โดยทั่วไปแล้วการแยกตัวจับคู่ไปยังตัวแปรจะไม่ได้ผลเพราะโดยปกติจะเปลี่ยนลำดับการโทร อย่างไรก็ตามการแยกตัวจับคู่เป็นวิธีการทำงานได้ดี
int between10And20 = and(gt(10), lt(20));
/* BAD */ when(foo.quux(anyInt(), between10And20)).thenReturn(true);
// Mockito sees the stack as the opposite: and(gt(10), lt(20)), anyInt().
public static int anyIntBetween10And20() { return and(gt(10), lt(20)); }
/* OK */ when(foo.quux(anyInt(), anyIntBetween10And20())).thenReturn(true);
// The helper method calls the matcher methods in the right order.
กองซ้อนเปลี่ยนแปลงบ่อยพอที่ Mockito ไม่สามารถตำรวจได้อย่างระมัดระวัง สามารถตรวจสอบสแต็กได้เฉพาะเมื่อคุณโต้ตอบกับ Mockito หรือล้อเลียนและต้องยอมรับตัวจับคู่โดยไม่ทราบว่าถูกใช้ทันทีหรือละทิ้งโดยไม่ตั้งใจ ตามทฤษฎีแล้วสแต็กควรว่างเปล่าเสมอนอกการเรียกwhen
หรือverify
แต่ Mockito ไม่สามารถตรวจสอบได้โดยอัตโนมัติ Mockito.validateMockitoUsage()
คุณสามารถตรวจสอบด้วยตนเอง
ในการโทรหาwhen
Mockito จะเรียกเมธอดที่เป็นปัญหาซึ่งจะทำให้เกิดข้อยกเว้นหากคุณได้ขีดฆ่าวิธีการที่จะโยนข้อยกเว้น (หรือต้องการค่าที่ไม่ใช่ศูนย์หรือไม่ใช่ค่าว่าง)
doReturn
และdoAnswer
(ฯลฯ ) ไม่เรียกใช้วิธีการจริงและมักเป็นทางเลือกที่มีประโยชน์
หากคุณเรียกวิธีการจำลองที่อยู่ตรงกลางของการeq
ขีดทับ(เช่นการคำนวณคำตอบสำหรับตัวจับคู่) Mockito จะตรวจสอบความยาวสแต็กเทียบกับการเรียกนั้นแทนและอาจล้มเหลว
หากคุณพยายามทำบางสิ่งที่ไม่ดีเช่นการขีดฆ่า/ การตรวจสอบวิธีสุดท้าย Mockito จะเรียกวิธีการจริงและทิ้งตัวจับคู่พิเศษไว้ในสแต็ก final
เรียกวิธีการอาจจะไม่โยนยกเว้น แต่คุณอาจจะได้รับการInvalidUseOfMatchersExceptionจาก matchers จรจัดเมื่อคุณมีปฏิสัมพันธ์ต่อไปด้วยจำลอง
ปัญหาทั่วไป
InvalidUseOfMatchersException :
ตรวจสอบว่าทุกอาร์กิวเมนต์มีการเรียกจับคู่เพียงครั้งเดียวถ้าคุณใช้ตัวจับคู่เลยและคุณไม่ได้ใช้ตัวจับคู่นอกการเรียกwhen
หรือ verify
ไม่ควรใช้ Matchers เป็นค่าที่ส่งคืนหรือฟิลด์ / ตัวแปร
ตรวจสอบว่าคุณไม่ได้เรียกการล้อเลียนซึ่งเป็นส่วนหนึ่งของการให้อาร์กิวเมนต์ที่ตรงกัน
ตรวจสอบว่าคุณไม่ได้พยายามขีดฆ่า / ยืนยันวิธีสุดท้ายด้วยตัวจับคู่ เป็นวิธีที่ดีในการทิ้งตัวจับคู่ไว้ในสแต็กและเว้นแต่วิธีสุดท้ายของคุณจะทำให้เกิดข้อยกเว้นนี่อาจเป็นครั้งเดียวที่คุณรู้ว่าวิธีที่คุณกำลังเยาะเย้ยถือเป็นที่สิ้นสุด
NullPointerException พร้อมอาร์กิวเมนต์ดั้งเดิม: (Integer) any()
คืนค่า null ในขณะที่any(Integer.class)
ส่งกลับ 0; สิ่งนี้อาจทำให้เกิดNullPointerException
ถ้าคุณคาดหวังว่าจะได้รับint
แทนที่จะเป็นจำนวนเต็ม ไม่ว่าในกรณีใดก็ตามชอบanyInt()
ซึ่งจะคืนค่าศูนย์และข้ามขั้นตอนการชกมวยอัตโนมัติ
NullPointerException หรือข้อยกเว้นอื่น ๆ : Call to when(foo.bar(any())).thenReturn(baz)
จะเรียก จริงfoo.bar(null)
ซึ่งคุณอาจถูกขีดฆ่าเพื่อโยนข้อยกเว้นเมื่อได้รับอาร์กิวเมนต์ว่าง สลับไปข้ามพฤติกรรมdoReturn(baz).when(foo).bar(any())
stubbed
การแก้ไขปัญหาทั่วไป
ใช้MockitoJUnitRunnerหรือเรียกอย่างชัดเจนvalidateMockitoUsage
ในวิธีtearDown
หรือของคุณ@After
(ซึ่งนักวิ่งจะทำเพื่อคุณโดยอัตโนมัติ) วิธีนี้จะช่วยตรวจสอบว่าคุณใช้ตัวจับคู่ผิดหรือไม่
สำหรับวัตถุประสงค์ในการแก้ไขจุดบกพร่องให้เพิ่มการโทรลงvalidateMockitoUsage
ในโค้ดของคุณโดยตรง สิ่งนี้จะส่งผลหากคุณมีอะไรในกองซึ่งเป็นสัญญาณเตือนที่ดีถึงอาการไม่ดี