Ví dụ về đối số của MockitoCaptor


141

Bất cứ ai cũng có thể vui lòng cung cấp cho tôi một ví dụ cho thấy cách sử dụng org.mockito.ArgumentCaptorlớp và nó khác với các công cụ đối sánh đơn giản được cung cấp với mockito.

Tôi đã đọc các tài liệu mockito được cung cấp nhưng chúng không minh họa rõ ràng, không ai trong số họ có thể giải thích rõ ràng.

Câu trả lời:


187

Tôi đồng ý với những gì @fge nói, hơn thế nữa. Hãy xem ví dụ. Xem xét bạn có một phương pháp:

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

Bây giờ nếu bạn muốn kiểm tra dữ liệu bên trong, bạn có thể sử dụng 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);

Nếu doSomething(data)đột biến innerData, thì sự thay đổi đó sẽ xuất hiện assertEquals("Some inner data", actual.innerData)hay sẽ innerDatađược ghi lại như trước khi doSomething được thực hiện?
Cory Klein

@CoryKlein Đây OtherClasslà một bản giả, và như được định nghĩa bây giờ, thực tế doSomething()sẽ không làm gì cả, nó chỉ đơn giản là ghi lại đối tượng đã được thông qua. Điều này có nghĩa là nó sẽ bị bắt như hiện tại trước khi doSomethingđược thực thi.
Slava Shpitalny 7/12/18

3
Trong verify, times(1)là mặc định và có thể được bỏ qua.
Inego

Làm thế nào để ArgumentCaptor biết rằng foo (khác) đã xảy ra vì nó được khởi tạo chỉ sau cuộc gọi foo (khác)?
AvramPop

1
@AvramPop người biết đây là đối tượng giả. Nó chứa bên trong rất nhiều thông tin về giả. Bên trong tất cả các thông tin đó, nó cũng chứa lịch sử cuộc gọi cho từng phương thức với các tham số của nó. Khi bạn gọi verifyphương thức, nó sử dụng thông tin đó để chạy khớp với xác minh mà bạn đang thực hiện. Đối với mỗi tham số, nó hỏi liệu nó có khớp với lệnh gọi cụ thể mà nó kiểm tra không. Khi ArgumentCaptor được kiểm tra, nó chỉ lưu trữ các giá trị mà nó được gọi để khi verifykết thúc, nó chứa tất cả các yêu cầu có liên quan. Đó là cách nó hoạt động. Hy vọng nó sẽ giúp
Slava Shpitalny

35

Hai điểm khác biệt chính là:

  • khi bạn nắm bắt ngay cả một đối số duy nhất, bạn có thể thực hiện các thử nghiệm phức tạp hơn nhiều đối với đối số này và với mã rõ ràng hơn;
  • một ArgumentCaptorcó thể chụp nhiều hơn một lần.

Để minh họa cái sau, hãy nói rằng bạn có:

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

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

Sau đó, trình quản lý sẽ có thể cung cấp cho bạn quyền truy cập vào tất cả 4 đối số, sau đó bạn có thể thực hiện các xác nhận riêng biệt.

Điều này hoặc bất kỳ số lượng đối số trong thực tế, vì a VerificationModekhông bị giới hạn trong một số lượng yêu cầu cố định; trong mọi trường hợp, người bắt giữ sẽ cung cấp cho bạn quyền truy cập vào tất cả chúng, nếu bạn muốn.

Điều này cũng có lợi ích là các bài kiểm tra như vậy (imho) dễ viết hơn nhiều so với việc phải thực hiện ArgumentMatchers của riêng bạn - đặc biệt nếu bạn kết hợp mockito với assertj.

Ồ, và vui lòng xem xét sử dụng TestNG thay vì JUnit.


1
Điều gì xảy ra nếu có nhiều tham số được truyền vào phương thức - tất cả các loại khác nhau? Làm thế nào để bạn thực sự xác minh rằng tham số boolean là đúng , ví dụ.
IgorGanapolsky

21
Bạn có thể đưa ra lời giải thích cho nhận xét của mình không: Ồ, và vui lòng xem xét sử dụng TestNG thay vì JUnit. . Tại sao phải xem xét nó? Tại sao thay đổi?
Navigatron

1
@IgorGanapolsky bạn chỉ cần thêm một ArgumentCaptor khác. ArgumentCaptor <BigDecimal> arg = ArgumentCaptor.forClass (BigDecimal. Class); ArgumentCaptor <String> arg2 = ArgumentCaptor.forClass (String. Class); Michael michael = Michael mới (); michael.sayHi (j); xác minh (j) .sayS Something (arg.capture (), arg2.capture ()); System.out.println ("giá trị là" + arg.getValue ()); System.out.println ("chuỗi là" + arg2.getValue ());
johnwick0831

12

Các bước để thực hiện kiểm tra đầy đủ là:

Chuẩn bị người bắt giữ:

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

xác minh lệnh gọi phụ thuộc vào thành phần (cộng tác viên của đối tượng được kiểm tra) lần (1), là giá trị mặc định, vì vậy ne cần thêm nó.

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

Nhận đối số được chuyển cho cộng tác viên

SomeArgumentClass someArgument = messageCaptor.getValue();

someArgument có thể được sử dụng để xác nhận


-2

Ở đây tôi cho bạn một ví dụ thích hợp về một phương thức gọi lại. vì vậy, giả sử chúng ta có một phương thức như phương thức login ():

 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");
}

Tôi cũng đặt tất cả các lớp của trình trợ giúp ở đây để làm cho ví dụ rõ ràng hơn: lớp 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);
}}

và chúng ta có ListListener lắng nghe như:

interface LoginListener {
void onLoginSuccess();

void onLoginFaliure();

}

bây giờ tôi chỉ muốn kiểm tra phương thức login () của lớp Đăng nhập

 @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);

cũng đừng quên thêm chú thích phía trên lớp kiểm tra như

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

1
Không nên tham khảo ArgumentCaptor?
Felipe Martins Melo

vâng, chúng tôi đang bắt người nghe chuyển đến phương thức login () trong ví dụ đăng nhập (LoginProvider loginProvider, LoginListener gọi lại)
Vikram singh

Đâu là captorđịnh nghĩa trong câu trả lời của bạn?
tom_mai78101

ArgumentCaptor <LoginListener> listenerCaptor = ArgumentCaptor.forClass (LoginListener. Class);
Vikram singh
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.