การใช้ Mockito เพื่อจำลองคลาสด้วยพารามิเตอร์ทั่วไป


280

มีวิธีทำความสะอาดแบบจำลองคลาสด้วยพารามิเตอร์ทั่วไปหรือไม่? ว่าฉันมีจะเยาะเย้ยชั้นเรียนซึ่งผมต้องผ่านเข้าไปในวิธีการที่คาดว่าจะเป็นFoo<T> Foo<Bar>ฉันสามารถทำสิ่งต่อไปนี้ได้ง่ายพอ:

Foo mockFoo = mock(Foo.class);
when(mockFoo.getValue).thenReturn(new Bar());

สมมติว่าผลตอบแทนประเภททั่วไปgetValue() แต่ที่จะมีลูกแมวเมื่อผมมารู้ทีหลังผ่านมันเข้าไปในวิธีการที่คาดหวังว่าT Foo<Bar>การคัดเลือกเป็นวิธีการเดียวในการทำเช่นนี้หรือไม่?

คำตอบ:


280

ฉันคิดว่าคุณต้องใช้มัน แต่ไม่ควรเลวร้ายเกินไป:

Foo<Bar> mockFoo = (Foo<Bar>) mock(Foo.class);
when(mockFoo.getValue()).thenReturn(new Bar());

34
ใช่ แต่คุณยังคงมีคำเตือน เป็นไปได้ไหมที่จะหลีกเลี่ยงคำเตือน?
odwl

12
@SuppressWarnings ("ไม่ จำกัด ")
qualidafial

18
ฉันคิดว่านี่เป็นที่ยอมรับอย่างเต็มที่เนื่องจากเรากำลังพูดถึงวัตถุจำลองในการทดสอบหน่วย
Magnilex

1
@demaniak มันไม่ทำงานเลย ไม่สามารถใช้ตัวจับคู่อาร์กิวเมนต์ในบริบทนั้นได้
Krzysztof Krasoń

1
@daniani ที่จะรวบรวมได้ดี แต่เมื่อใช้การทดสอบก็จะโยน InvalidUseOfMatchersException (ซึ่งเป็น RuntimeException)
Superole

277

อีกวิธีหนึ่งในการทำเช่นนี้คือการใช้@Mockคำอธิบายประกอบแทน ไม่ได้ผลในทุกกรณี แต่ดูเซ็กซี่กว่า :)

นี่คือตัวอย่าง:

@RunWith(MockitoJUnitRunner.class)
public class FooTests {

    @Mock
    public Foo<Bar> fooMock;

    @Test
    public void testFoo() {
        when(fooMock.getValue()).thenReturn(new Bar());
    }
}

ต้นฟิลด์กำกับด้วยMockitoJUnitRunner@Mock


3
เลิกใช้แล้วใน 1.9.5 :( ดูเหมือนว่าจะสะอาดกว่าสำหรับฉัน
รหัส Novitiate

12
@CodeNovitiate ฉันไม่พบคำอธิบายประกอบที่เลิกใช้แล้วใน MockitoJUnitRunner และ Mock ใน 1.9.5 ดังนั้นสิ่งที่ถูกคัดค้าน? (ใช่แล้ว org.mockito.MockitoAnnotations.Mock เลิกใช้แล้ว แต่คุณควรใช้ org.mockito.Mock แทน)
neu242

12
ทำได้ดีมากมันใช้งานได้ดีสำหรับฉัน มันไม่ได้เป็นเพียงแค่ "เซ็กซี่" SuppressWarningsก็หลีกเลี่ยงการเตือนภัยโดยไม่ต้องใช้ คำเตือนมีอยู่ด้วยเหตุผลที่ดีกว่าที่จะไม่อยู่ในนิสัยของการปราบปรามพวกเขา ขอบคุณ!
Nicole

4
มีสิ่งหนึ่งที่ฉันไม่ชอบเกี่ยวกับการใช้@Mockแทนmock(): เขตข้อมูลยังคงเป็นโมฆะในระหว่างการก่อสร้างดังนั้นฉันจึงไม่สามารถแทรกการอ้างอิงในเวลานั้นและไม่สามารถทำให้ฟิลด์สุดท้ายได้ อดีตสามารถแก้ไขได้ด้วย@Beforeวิธีการ - ประกาศแน่นอน
Rüdiger Schulz

3
สำหรับการเริ่มต้นเพียงโทร MockitoAnnotations.initMocks (นี้);
borjab

42

คุณสามารถสร้างคลาส / อินเตอร์เฟสระดับกลางที่จะตอบสนองประเภททั่วไปที่คุณต้องการระบุ ตัวอย่างเช่นถ้า Foo เป็นส่วนต่อประสานคุณสามารถสร้างส่วนต่อไปนี้ในชั้นทดสอบของคุณ

private interface FooBar extends Foo<Bar>
{
}

ในสถานการณ์ที่ Foo เป็นชั้นเรียนที่ไม่ใช่ครั้งสุดท้ายคุณสามารถขยายชั้นเรียนด้วยรหัสต่อไปนี้และทำสิ่งเดียวกัน:

public class FooBar extends Foo<Bar>
{
}

จากนั้นคุณสามารถใช้ตัวอย่างใดตัวอย่างหนึ่งข้างต้นด้วยรหัสต่อไปนี้:

Foo<Bar> mockFoo = mock(FooBar.class);
when(mockFoo.getValue()).thenReturn(new Bar());

4
ให้Fooเป็นอินเทอร์เฟซหรือคลาสที่ไม่ใช่ครั้งสุดท้ายนี้ดูเหมือนจะเป็นทางออกที่สวยงามพอสมควร ขอบคุณ
Tim Clemons

ฉันอัพเดตคำตอบเพื่อรวมตัวอย่างสำหรับคลาสที่ไม่สิ้นสุดด้วย เป็นการดีที่คุณจะเข้ารหัสกับอินเทอร์เฟซ แต่ก็ไม่ได้เป็นเช่นนั้นเสมอไป จับดี!
dsingleton

16

สร้างวิธีการทดสอบยูทิลิตี้ มีประโยชน์เป็นพิเศษหากคุณต้องการมากกว่าหนึ่งครั้ง

@Test
public void testMyTest() {
    // ...
    Foo<Bar> mockFooBar = mockFoo();
    when(mockFooBar.getValue).thenReturn(new Bar());

    Foo<Baz> mockFooBaz = mockFoo();
    when(mockFooBaz.getValue).thenReturn(new Baz());

    Foo<Qux> mockFooQux = mockFoo();
    when(mockFooQux.getValue).thenReturn(new Qux());
    // ...
}

@SuppressWarnings("unchecked") // still needed :( but just once :)
private <T> Foo<T> mockFoo() {
    return mock(Foo.class);
}

สามารถขยายคำตอบของคุณเพื่อสร้างวิธีอรรถประโยชน์ทั่วไปที่ส่งผ่านในคลาสที่คุณต้องการเยาะเย้ย
William Dutton

1
@WilliamDutton static <T> T genericMock(Class<? super T> classToMock) { return (T)mock(classToMock); }มันไม่จำเป็นต้องมีการปราบปรามเดียว :) แต่ต้องระวังInteger num = genericMock(Number.class)compiles ClassCastExceptionแต่พ่น สิ่งนี้มีประโยชน์สำหรับG<P> mock = mock(G.class)กรณีที่พบบ่อยที่สุดเท่านั้น
TWiStErRob

6

ฉันยอมรับว่าไม่ควรระงับคำเตือนในชั้นเรียนหรือวิธีการใดวิธีหนึ่งเนื่องจากอาจมองข้ามคำเตือนอื่น ๆ ที่ระงับโดยไม่ตั้งใจ แต่ IMHO มันสมเหตุสมผลอย่างยิ่งที่จะระงับการเตือนที่มีผลกับรหัสบรรทัดเดียวเท่านั้น

@SuppressWarnings("unchecked")
Foo<Bar> mockFoo = mock(Foo.class);

3

นี่เป็นกรณีที่น่าสนใจ: เมธอดรับการรวบรวมทั่วไปและส่งคืนการรวบรวมทั่วไปที่มีชนิดฐานเดียวกัน ตัวอย่างเช่น:

Collection<? extends Assertion> map(Collection<? extends Assertion> assertions);

วิธีนี้สามารถเยาะเย้ยด้วยการรวมกันของ Mockito anyCollectionOf matcher และคำตอบ

when(mockedObject.map(anyCollectionOf(Assertion.class))).thenAnswer(
     new Answer<Collection<Assertion>>() {
         @Override
         public Collection<Assertion> answer(InvocationOnMock invocation) throws Throwable {
             return new ArrayList<Assertion>();
         }
     });
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.