วิธีการจำลองโมฆะด้วย Mockito


938

วิธีการเยาะเย้ยวิธีการที่มีประเภทผลตอบแทนเป็นโมฆะ?

ฉันใช้รูปแบบการสังเกตการณ์ แต่ไม่สามารถเยาะเย้ยกับ Mockito ได้เพราะฉันไม่รู้ว่าจะทำอย่างไร

และฉันพยายามค้นหาตัวอย่างบนอินเทอร์เน็ต แต่ไม่ประสบความสำเร็จ

ชั้นเรียนของฉันมีลักษณะเช่นนี้:

public class World {

    List<Listener> listeners;

    void addListener(Listener item) {
        listeners.add(item);
    }

    void doAction(Action goal,Object obj) {
        setState("i received");
        goal.doAction(obj);
        setState("i finished");
    }

    private string state;
    //setter getter state
} 

public class WorldTest implements Listener {

    @Test public void word{
    World  w= mock(World.class);
    w.addListener(this);
    ...
    ...

    }
}

interface Listener {
    void doAction();
}

ระบบไม่ถูกกระตุ้นด้วยการเยาะเย้ย

ฉันต้องการที่จะแสดงสถานะของระบบดังกล่าวข้างต้น และยืนยันตามที่พวกเขา


6
ระวังว่าวิธีการโมฆะบน mocks ไม่ได้ทำอะไรเป็นค่าเริ่มต้น!
บรรทัดที่

1
@ Line นั่นคือสิ่งที่ฉันกำลังมองหา ดูเหมือนชัดเจนหลังจากที่คุณพูด แต่มันเน้นหลักการเยาะเย้ย: คุณจะต้องใช้วิธีการจำลองคลาสที่เยาะเย้ยเพื่อเอฟเฟกต์ของพวกเขาเช่นค่าตอบแทนหรือข้อยกเว้น ขอบคุณ!
allenjom

คำตอบ:


1145

ลองดูที่ Mockito เอกสาร API เป็นเอกสารที่เชื่อมโยงกล่าว (จุด # 12) คุณสามารถใช้ใด ๆ ของdoThrow(), doAnswer(), doNothing(), doReturn()ครอบครัวของวิธีการจากกรอบ Mockito จะเยาะเย้ยวิธีการเป็นโมฆะ

ตัวอย่างเช่น,

Mockito.doThrow(new Exception()).when(instance).methodName();

หรือถ้าคุณต้องการที่จะรวมมันกับพฤติกรรมการติดตาม

Mockito.doThrow(new Exception()).doNothing().when(instance).methodName();

ทะนงว่าคุณกำลังมองหาที่เยาะเย้ยหมาsetState(String s)ในระดับโลกดังต่อไปนี้คือการใช้รหัสวิธีการในการเยาะเย้ยdoAnswersetState

World  mockWorld = mock(World.class); 
doAnswer(new Answer<Void>() {
    public Void answer(InvocationOnMock invocation) {
      Object[] args = invocation.getArguments();
      System.out.println("called with arguments: " + Arrays.toString(args));
      return null;
    }
}).when(mockWorld).setState(anyString());

8
@qualidafial: ใช่ฉันคิดว่าการแปรสภาพเป็นโมฆะจะดีกว่าเพราะบ่งบอกได้ดีกว่าว่าฉันไม่สนใจประเภทผลตอบแทน ฉันไม่ได้ตระหนักถึงโครงสร้างนี้ขอบคุณที่ชี้ให้เห็น
sateesh

2
doThrow เป็น # 5 ในขณะนี้ (สำหรับฉันด้วยการใช้ doThrow สิ่งนี้ได้แก้ไขข้อความ "'void' ไม่อนุญาตให้ใช้ที่นี่" สำหรับผู้ติดตาม ... )
rogerdpack

@qualidafial: ฉันคิดว่าประเภทการตอบกลับของการโทร Answer.answer ไม่ใช่สิ่งที่ได้รับกลับคืนสู่วิธีการเดิม แต่เป็นสิ่งที่ถูกส่งกลับไปยังการเรียกใช้ doAnswer ซึ่งน่าจะเป็นไปได้ว่าคุณต้องการทำสิ่งอื่นด้วยค่านั้นในการทดสอบของคุณ
12:00 น

1
:( ในการพยายามจำลอง RateLimiter.java เวอร์ชัน 16.0.1 ใน guava doNothing () เมื่อ (mockLimiterReject) .setRate (100) ส่งผลให้การเรียกใช้ setRate ของ RateLimiter ทำให้เกิด nullpointer เนื่องจาก mutex นั้นเป็นโมฆะด้วยเหตุผลบางประการ มันจึงไม่ได้เยาะเย้ยวิธี setRate ของฉัน :( แต่แทนที่จะเรียกว่า :(
Dean Hiller

2
@DeanHiller สังเกตเห็นว่าsetRate()เป็นfinalเช่นนั้นและดังนั้นจึงไม่สามารถเยาะเย้ย ลองcreate()ใช้อินสแตนซ์ที่ทำในสิ่งที่คุณต้องการแทน RateLimiterไม่ควรมีความจำเป็นที่จะเยาะเย้ย
dimo414

113

ฉันคิดว่าฉันพบคำตอบที่ง่ายกว่าสำหรับคำถามนั้นเพื่อเรียกวิธีการที่แท้จริงเพียงวิธีเดียว (แม้ว่าจะได้รับคืนเป็นโมฆะ) คุณสามารถทำได้:

Mockito.doCallRealMethod().when(<objectInstance>).<method>();
<objectInstance>.<method>();

หรือคุณสามารถเรียกวิธีการจริงสำหรับวิธีการทั้งหมดของชั้นเรียนที่ทำสิ่งนี้:

<Object> <objectInstance> = mock(<Object>.class, Mockito.CALLS_REAL_METHODS);

13
นี่คือคำตอบที่แท้จริงที่นี่ วิธีสอดแนม () ทำงานได้ดี แต่โดยทั่วไปจะสงวนไว้เมื่อคุณต้องการให้วัตถุทำทุกอย่างได้ตามปกติ
biggusjimmus

1
สิ่งนี้หมายความว่า? คุณกำลังเรียกวิธีการจริง ๆ หรือไม่ ฉันไม่เคยใช้ mockito มาก่อนจริงๆ
obesechicken13

ใช่การเยาะเย้ยจะเรียกวิธีการจริง หากคุณใช้ @Mock คุณสามารถระบุเช่นเดียวกันกับ: @Mock (answer = Answers.CALLS_REAL_METHODS) เพื่อให้ได้ผลลัพธ์เดียวกัน
Ale

70

การเพิ่มสิ่งที่ @sateesh พูดเมื่อคุณเพียงแค่ต้องการจำลองวิธีการโมฆะเพื่อป้องกันไม่ให้การทดสอบเรียกคุณสามารถใช้Spyวิธีนี้:

World world = new World();
World spy = Mockito.spy(world);
Mockito.doNothing().when(spy).methodToMock();

เมื่อคุณต้องการเรียกใช้การทดสอบของคุณให้แน่ใจว่าคุณเรียกวิธีการในการทดสอบกับspyวัตถุและไม่ได้อยู่ในworldวัตถุ ตัวอย่างเช่น:

assertEquals(0,spy.methodToTestThatShouldReturnZero());

57

วิธีการแก้ปัญหาที่เรียกว่าปัญหาที่เกิดขึ้นคือการใช้spy Mockito.spy ( ... )แทนการmock Mockito.mock ( .. )

Spy ช่วยให้เราสามารถเยาะเย้ยบางส่วน Mockito เก่งในเรื่องนี้ เนื่องจากคุณมีชั้นเรียนที่ไม่สมบูรณ์ด้วยวิธีนี้คุณจำลองสถานที่ที่จำเป็นในชั้นนี้


3
ฉันสะดุดที่นี่เพราะฉันมีปัญหาที่คล้ายกัน (เช่นกันเกิดขึ้นเพื่อทดสอบปฏิสัมพันธ์เรื่อง / สังเกตการณ์) ฉันใช้สายลับอยู่แล้ว แต่ฉันต้องการวิธีการ 'เปลี่ยนวิชา' เพื่อทำสิ่งที่แตกต่าง ฉันสามารถใช้ `Verify (ผู้สังเกตการณ์). SubjectChanged (หัวเรื่อง) เพียงเพื่อดูว่าวิธีการนั้นถูกเรียก แต่ด้วยเหตุผลบางอย่างฉันค่อนข้างจะแทนที่วิธีการ สำหรับที่มีการรวมกันของวิธีการ Sateesh และคำตอบของคุณที่นี่เป็นวิธีที่จะไป ...
gMale

32
ไม่การทำเช่นนี้จะไม่ช่วยในการเย้ยหยันวิธีการที่เป็นโมฆะ เคล็ดลับคือการใช้หนึ่งในสี่วิธี Mockito คงที่ระบุไว้ในคำตอบของ sateesh
Dawood ibn Kareem

2
@Gurnard สำหรับคำถามของคุณจะดูที่นี้stackoverflow.com/questions/1087339/...
ibrahimyilmaz

ปริมาณนี้ใช้งานได้จริง
Nilotpal

34

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

import static org.mockito.Mockito.*;

สำหรับการเยาะเย้ยบางส่วนและยังคงรักษาฟังก์ชั่นดั้งเดิมในส่วนที่เหลือ mockito เสนอ "Spy"

คุณสามารถใช้มันได้ดังต่อไปนี้:

private World world = spy(World.class);

ในการกำจัดวิธีการที่ถูกดำเนินการคุณสามารถใช้สิ่งนี้:

doNothing().when(someObject).someMethod(anyObject());

เพื่อให้พฤติกรรมที่กำหนดเองบางอย่างกับวิธีการใช้ "เมื่อ" กับ "แล้วกลับ":

doReturn("something").when(this.world).someMethod(anyObject());

สำหรับตัวอย่างเพิ่มเติมโปรดค้นหาตัวอย่าง mockito ที่ยอดเยี่ยมในเอกสาร


4
การนำเข้าคงที่เกี่ยวข้องกับการทำให้สามารถอ่านได้มากขึ้น?
jsonbourne

2
ไม่มีอะไรแท้จริง
specializt

2
ฉันคิดว่ามันเป็นเรื่องของรสนิยมฉันแค่อยากให้ประโยคดูเหมือน (เกือบ) เหมือนประโยคภาษาอังกฤษและ Class.methodname (). some () vs. methodname () .something บางอย่างอ่านไม่คล่อง
fl0w

1
หากคุณทำงานกับทีมการนำเข้าคงที่ทำให้เกิดความเจ็บปวดในตูดเพื่อให้คนอื่นดูว่าวิธีการมาจากที่ใดเนื่องจากมักจะมีสัญลักษณ์ตัวแทนในการนำเข้า
LowKeyEnergy

ถูกต้องอย่างไรก็ตามสำหรับรหัสทดสอบและ Mockito สิ่งนี้ควรชัดเจนและไม่ใช่ปัญหา
fl0w

27

วิธีการจำลองวิธีการโมฆะด้วย mockito - มีสองตัวเลือก:

  1. doAnswer - หากเราต้องการวิธีการเยาะเย้ยโมฆะ (เยาะเย้ยพฤติกรรมแม้จะเป็นโมฆะ)
  2. doThrow- Mockito.doThrow()ถ้าคุณต้องการที่จะโยนข้อยกเว้นจากวิธีการเยาะเย้ยเป็นโมฆะ

ต่อไปนี้เป็นตัวอย่างของวิธีการใช้งาน (ไม่ใช่ usecase ในอุดมคติ แต่ต้องการแสดงการใช้งานพื้นฐาน)

@Test
public void testUpdate() {

    doAnswer(new Answer<Void>() {

        @Override
        public Void answer(InvocationOnMock invocation) throws Throwable {
            Object[] arguments = invocation.getArguments();
            if (arguments != null && arguments.length > 1 && arguments[0] != null && arguments[1] != null) {

                Customer customer = (Customer) arguments[0];
                String email = (String) arguments[1];
                customer.setEmail(email);

            }
            return null;
        }
    }).when(daoMock).updateEmail(any(Customer.class), any(String.class));

    // calling the method under test
    Customer customer = service.changeEmail("old@test.com", "new@test.com");

    //some asserts
    assertThat(customer, is(notNullValue()));
    assertThat(customer.getEmail(), is(equalTo("new@test.com")));

}

@Test(expected = RuntimeException.class)
public void testUpdate_throwsException() {

    doThrow(RuntimeException.class).when(daoMock).updateEmail(any(Customer.class), any(String.class));

    // calling the method under test
    Customer customer = service.changeEmail("old@test.com", "new@test.com");

}
}

คุณสามารถหารายละเอียดเพิ่มเติมเกี่ยวกับวิธีการเยาะเย้ยและทดสอบ วิธีการโมฆะด้วย Mockito ในโพสต์ของฉันวิธีการเยาะเย้ยกับ Mockito (คู่มือที่ครอบคลุมพร้อมตัวอย่าง)


1
ตัวอย่างที่ดี หมายเหตุ: ใน java 8 อาจจะดีกว่าที่จะใช้แลมบ์ดาแทนการใช้คลาสที่ไม่ระบุชื่อ: 'doAnswer ((ตอบ <Void>) การเรียก -> {// CODE}) เมื่อ (mockInstance) .add (เมธอด ());
miwe

16

กำลังเพิ่มคำตอบให้กับกลุ่มอื่น (ไม่ได้ตั้งใจให้เล่นสำนวน) ...

คุณต้องเรียกใช้วิธีการ doAnswer หากคุณไม่สามารถ \ ไม่ต้องการใช้สายลับ แต่คุณไม่จำเป็นต้องที่จะม้วนของคุณเองคำตอบ มีการใช้งานเริ่มต้นหลายอย่าง ยวดCallsRealMethods

ในทางปฏิบัติมันมีลักษณะดังนี้:

doAnswer(new CallsRealMethods()).when(mock)
        .voidMethod(any(SomeParamClass.class));

หรือ:

doAnswer(Answers.CALLS_REAL_METHODS.get()).when(mock)
        .voidMethod(any(SomeParamClass.class));

16

ใน Java 8 คุณสามารถทำความสะอาดได้เล็กน้อยโดยสมมติว่าคุณมีการนำเข้าแบบคงที่สำหรับorg.mockito.Mockito.doAnswer:

doAnswer(i -> {
  // Do stuff with i.getArguments() here
  return null;
}).when(*mock*).*method*(*methodArguments*);

เป็นสิ่งสำคัญและไม่ได้รวบรวมจะล้มเหลวกับข้อผิดพลาดปิดบังบางอย่างเป็นธรรมขณะที่มันจะไม่สามารถที่จะหาแทนที่เหมาะสำหรับreturn null;doAnswer

ตัวอย่างเช่นการExecutorServiceดำเนินการใด ๆ ที่Runnableส่งผ่านไปทันทีexecute()สามารถนำมาใช้โดยใช้:

doAnswer(i -> {
  ((Runnable) i.getArguments()[0]).run();
  return null;
}).when(executor).execute(any());

2
ในหนึ่งบรรทัด: Mockito.doAnswer ((i) -> null) เมื่อใด (อินสแตนซ์) .method (any ());
Akshay Thorve

@AkshayThorve มันไม่ทำงานเมื่อคุณต้องการทำอะไรกับฉัน
ทิม B

7

ฉันคิดว่าปัญหาของคุณเกิดจากโครงสร้างการทดสอบของคุณ ฉันพบว่ามันยากที่จะผสมการเยาะเย้ยกับวิธีดั้งเดิมของการใช้อินเทอร์เฟซในชั้นทดสอบ (ตามที่คุณทำที่นี่)

หากคุณใช้ฟังเป็น Mock คุณสามารถตรวจสอบการโต้ตอบ

Listener listener = mock(Listener.class);
w.addListener(listener);
world.doAction(..);
verify(listener).doAction();

สิ่งนี้จะทำให้คุณพึงพอใจว่า 'โลก' กำลังทำสิ่งที่ถูกต้อง


0

ใช้ Mockito.doThrow เช่นเดียวกับใน:

Mockito.doThrow(new Exception()).when(instance).methodName();

คุณสามารถลองตัวอย่างที่ดีนี้:

public void testCloseStreamOnException() throws Exception {
    OutputStream outputStream = Mockito.mock(OutputStream.class);
    IFileOutputStream ifos = new IFileOutputStream(outputStream);
    Mockito.doThrow(new IOException("Dummy Exception")).when(outputStream).flush();
    try {
      ifos.close();
      fail("IOException is not thrown");
    } catch (IOException ioe) {
      assertEquals("Dummy Exception", ioe.getMessage());
    }
    Mockito.verify(outputStream).close();
  }

ที่มา: http://apisonar.com/java-examples/org.mockito.Mockito.doThrow.html#Example-19

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