ตัวอย่าง argumentCaptor ของ Mockito


141

ทุกคนช่วยยกตัวอย่างให้ฉันดูวิธีใช้org.mockito.ArgumentCaptorคลาสได้อย่างไรและมันแตกต่างจากแมทช์ง่ายๆที่มาพร้อมกับ mockito

ฉันอ่านเอกสาร mockito ที่ให้มา แต่เอกสารเหล่านั้นไม่ได้อธิบายอย่างชัดเจนไม่มีผู้ใดสามารถอธิบายได้อย่างชัดเจน

คำตอบ:


187

ฉันเห็นด้วยกับสิ่งที่ @fge พูดมากกว่านั้น ให้ดูตัวอย่าง พิจารณาว่าคุณมีวิธี:

class A {
    public void foo(OtherClass other) {
        SomeData data = new SomeData("Some inner data");
        other.doSomething(data);
    }
}

ตอนนี้ถ้าคุณต้องการตรวจสอบข้อมูลภายในคุณสามารถใช้ captor:

// Create a mock of the OtherClass
OtherClass other = mock(OtherClass.class);

// Run the foo method with the mock
new A().foo(other);

// Capture the argument of the doSomething function
ArgumentCaptor<SomeData> captor = ArgumentCaptor.forClass(SomeData.class);
verify(other, times(1)).doSomething(captor.capture());

// Assert the argument
SomeData actual = captor.getValue();
assertEquals("Some inner data", actual.innerData);

หากการdoSomething(data)กลายพันธุ์innerDataการเปลี่ยนแปลงนั้นจะเกิดassertEquals("Some inner data", actual.innerData)ขึ้นหรือจะinnerDataถูกจับเหมือนเดิมก่อนที่จะ doSomethingถูกประหารชีวิต?
คอรีไคลน์

@CoryKlein The OtherClassเป็นจำลองและตามที่กำหนดไว้ในขณะนี้doSomething()จะไม่จริงทำอะไรมันเพียงบันทึกวัตถุที่ผ่าน ซึ่งหมายความว่าจะถูกจับตามสภาพเดิมก่อนที่จะdoSomethingถูกดำเนินการ
Slava Shpitalny

3
ในverify, times(1)เป็นค่าเริ่มต้นและสามารถละเว้น
Inego

ArgumentCaptor รู้ได้อย่างไรว่า foo (อื่น ๆ ) เกิดขึ้นเนื่องจากถูกทำให้เป็นอินสแตนซ์เฉพาะหลังจากการเรียก foo (อื่น ๆ )
AvramPop

1
@AvramPop ผู้ที่รู้ว่านี่คือวัตถุจำลอง มันมีข้อมูลจำนวนมากเกี่ยวกับการเยาะเย้ย ภายในข้อมูลทั้งหมดนั้นยังมีประวัติการโทรสำหรับแต่ละวิธีด้วยพารามิเตอร์ เมื่อคุณเรียกใช้verifyเมธอดมันจะใช้ข้อมูลนั้นเพื่อทำการจับคู่กับการตรวจสอบที่คุณทำ สำหรับทุกพารามิเตอร์มันถามว่ามันตรงกับสายเฉพาะมันตรวจสอบ เมื่อมีการตรวจสอบ ArgumentCaptor มันก็จะเก็บค่าที่มันถูกเรียกด้วยดังนั้นเมื่อverifyสิ้นสุดมันจะเก็บการร้องขอที่เกี่ยวข้องทั้งหมด มันเป็นวิธีการทำงานคร่าวๆ หวังว่ามันจะช่วย
Slava Shpitalny

35

ความแตกต่างที่สำคัญสองประการคือ:

  • เมื่อคุณจับแม้แต่การโต้แย้งเดียวคุณสามารถทำการทดสอบที่ซับซ้อนมากขึ้นในการโต้แย้งนี้และด้วยรหัสที่ชัดเจนยิ่งขึ้น
  • ArgumentCaptorสามารถจับภาพมากขึ้นมากกว่าหนึ่งครั้ง

หากต้องการแสดงตัวอย่างหลังให้พูดว่าคุณมี:

final ArgumentCaptor<Foo> captor = ArgumentCaptor.forClass(Foo.class);

verify(x, times(4)).someMethod(captor.capture()); // for instance

จากนั้นผู้จับกุมจะสามารถให้คุณเข้าถึงข้อโต้แย้งทั้ง 4 ข้อซึ่งคุณสามารถทำการยืนยันแยกต่างหากได้

ความจริงข้อนี้หรือข้อโต้แย้งใด ๆ เนื่องจาก a VerificationModeไม่ได้ จำกัด อยู่ที่การร้องขอจำนวนคงที่ ไม่ว่าในกรณีใดผู้จับกุมจะให้สิทธิ์การเข้าถึงแก่คุณหากคุณต้องการ

นี่ยังมีประโยชน์ที่การทดสอบดังกล่าวนั้นง่ายกว่าการเขียนของคุณเองArgumentMatcherโดยเฉพาะอย่างยิ่งถ้าคุณรวม mockito กับ assertj

โอ้และโปรดพิจารณาใช้ TestNG แทน JUnit


1
เกิดอะไรขึ้นถ้ามีหลายพารามิเตอร์ที่ส่งผ่านไปยังเมธอด - ชนิดที่แตกต่างกันทั้งหมด คุณจะตรวจสอบว่าพารามิเตอร์บูลีนเป็นจริงได้อย่างไร
IgorGanapolsky

21
คุณสามารถให้คำอธิบายสำหรับความคิดเห็นของคุณ: โอ้และโปรดพิจารณาใช้ TestNG แทน JUnit . พิจารณามันทำไม ทำไมการเปลี่ยนแปลง
Navigatron

1
@IgorGanapolsky คุณเพียงแค่เพิ่ม ArgumentCaptor อีก ArgumentCaptor <BigDecimal> arg = ArgumentCaptor.forClass (BigDecimal.class); ArgumentCaptor <String> arg2 = ArgumentCaptor.forClass (String.class); Michael michael = new Michael (); michael.sayHi (ญ); ตรวจสอบ (j) .saySomething (arg.capture (), arg2.capture ()); System.out.println ("value is" + arg.getValue ()); System.out.println ("string is" + arg2.getValue ());
johnwick0831

12

ขั้นตอนในการตรวจสอบเต็มรูปแบบคือ:

เตรียมแคปเตอร์:

ArgumentCaptor<SomeArgumentClass> someArgumentCaptor = ArgumentCaptor.forClass(SomeArgumentClass.class);

ตรวจสอบการเรียกขึ้นอยู่กับองค์ประกอบ (ผู้ทำงานร่วมกันของเรื่องภายใต้การทดสอบ) ครั้ง (1) เป็นค่าเริ่มต้นดังนั้นจึงจำเป็นต้องเพิ่ม

verify(dependentOnComponent, times(1)).send(someArgumentCaptor.capture());

รับอาร์กิวเมนต์ที่ส่งผ่านไปยังผู้ทำงานร่วมกัน

SomeArgumentClass someArgument = messageCaptor.getValue();

someArgument สามารถใช้สำหรับการยืนยัน


-2

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

 public void login() {
    loginService = new LoginService();
    loginService.login(loginProvider, new LoginListener() {
        @Override
        public void onLoginSuccess() {
            loginService.getresult(true);
        }

        @Override
        public void onLoginFaliure() {
            loginService.getresult(false);

        }
    });
    System.out.print("@@##### get called");
}

ฉันยังใส่คลาสผู้ช่วยทั้งหมดที่นี่เพื่อทำให้ตัวอย่างชัดเจนยิ่งขึ้น: คลาส loginService

public class LoginService implements Login.getresult{
public void login(LoginProvider loginProvider,LoginListener callback){

    String username  = loginProvider.getUsername();
    String pwd  = loginProvider.getPassword();
    if(username != null && pwd != null){
        callback.onLoginSuccess();
    }else{
        callback.onLoginFaliure();
    }

}

@Override
public void getresult(boolean value) {
    System.out.print("login success"+value);
}}

และเรามีผู้ฟัง LoginListener เป็น:

interface LoginListener {
void onLoginSuccess();

void onLoginFaliure();

}

ตอนนี้ฉันแค่ต้องการทดสอบวิธีการเข้าสู่ระบบ () ของการเข้าชั้นเรียน

 @Test
public void loginTest() throws Exception {
    LoginService service = mock(LoginService.class);
    LoginProvider provider = mock(LoginProvider.class);
    whenNew(LoginProvider.class).withNoArguments().thenReturn(provider);
    whenNew(LoginService.class).withNoArguments().thenReturn(service);
    when(provider.getPassword()).thenReturn("pwd");
    when(provider.getUsername()).thenReturn("username");
    login.getLoginDetail("username","password");

    verify(provider).setPassword("password");
    verify(provider).setUsername("username");

    verify(service).login(eq(provider),captor.capture());

    LoginListener listener = captor.getValue();

    listener.onLoginSuccess();

    verify(service).getresult(true);

อย่าลืมเพิ่มคำอธิบายประกอบด้านบนคลาสการทดสอบด้วย

@RunWith(PowerMockRunner.class)
@PrepareForTest(Login.class)

1
ไม่ควรอ้างถึง ArgumentCaptor?
เฟลิเป้มาร์ตินส์เมลโล่

ใช่เรากำลังรวบรวมผู้ฟังที่ส่งผ่านไปยังวิธีการเข้าสู่ระบบ () ในตัวอย่างการเข้าสู่ระบบ (LoginProvider loginProvider, CallbackListListener)
Vikram singh

ที่ไหนcaptorที่กำหนดไว้ในคำตอบของคุณ?
tom_mai78101

ArgumentCaptor <LoginListener> listenerCaptor = ArgumentCaptor.forClass (LoginListener.class);
Vikram singh
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.