Tạo một phương thức bị chế giễu trả về một đối số được truyền cho nó


673

Hãy xem xét một chữ ký phương thức như:

public String myFunction(String abc);

Mockito có thể giúp trả về cùng một chuỗi mà phương thức nhận được không?


Ok, nói chung về bất kỳ khung mô phỏng java nào nói chung ... Điều này có thể xảy ra với bất kỳ khung công tác nào khác không, hay tôi chỉ nên tạo một cuống câm để bắt chước hành vi tôi muốn?
Abhijeet Kashnia

Câu trả lời:


1000

Bạn có thể tạo một câu trả lời trong Mockito. Giả sử, chúng ta có một giao diện có tên Ứng dụng với phương thức myFactor.

public interface Application {
  public String myFunction(String abc);
}

Đây là phương pháp thử nghiệm với câu trả lời Mockito:

public void testMyFunction() throws Exception {
  Application mock = mock(Application.class);
  when(mock.myFunction(anyString())).thenAnswer(new Answer<String>() {
    @Override
    public String answer(InvocationOnMock invocation) throws Throwable {
      Object[] args = invocation.getArguments();
      return (String) args[0];
    }
  });

  assertEquals("someString",mock.myFunction("someString"));
  assertEquals("anotherString",mock.myFunction("anotherString"));
}

Kể từ Mockito 1.9.5 và Java 8, bạn cũng có thể sử dụng biểu thức lambda:

when(myMock.myFunction(anyString())).thenAnswer(i -> i.getArguments()[0]);

1
Đây là những gì tôi đang tìm kiếm, quá. Cảm ơn bạn! Vấn đề của tôi là khác nhau, mặc dù. Tôi muốn giả định một dịch vụ kiên trì (EJB) lưu trữ các đối tượng và trả về chúng theo tên.
Migu

7
Tôi đã tạo ra một lớp học thêm kết thúc việc tạo ra câu trả lời. Vì vậy, mã này đọc nhưwhen(...).then(Return.firstParameter())
SpaceTrucker

69
Với lambdas Java 8, thật dễ dàng để trả về đối số đầu tiên, ngay cả đối với lớp cụ thể, nghĩa là when(foo(any()).then(i -> i.getArgumentAt(0, Bar.class)). Và bạn cũng có thể sử dụng một tham chiếu phương thức và gọi phương thức thực.
Paweł Dyda

Điều này giải quyết vấn đề của tôi với một phương thức trả về Iterator<? extends ClassName>gây ra tất cả các loại vấn đề trong một thenReturn()câu lệnh.
Michael Storesin

16
Với Java 8 và Mockito <1.9.5 thì câu trả lời của Paweł trở thànhwhen(foo(any()).thenAnswer(i -> i.getArguments()[0])
Graeme Moss

566

Nếu bạn có Mockito 1.9.5 trở lên, có một phương thức tĩnh mới có thể tạo Answerđối tượng cho bạn. Bạn cần phải viết một cái gì đó như

import static org.mockito.Mockito.when;
import static org.mockito.AdditionalAnswers.returnsFirstArg;

when(myMock.myFunction(anyString())).then(returnsFirstArg());

Hay cách khác

doAnswer(returnsFirstArg()).when(myMock).myFunction(anyString());

Lưu ý rằng returnsFirstArg()phương thức này là tĩnh trong AdditionalAnswerslớp, mới đối với Mockito 1.9.5; vì vậy bạn sẽ cần nhập tĩnh đúng.


17
Lưu ý: đó là when(...).then(returnsFirstArg()), tôi đã nhầm lẫn đã when(...).thenReturn(returnsFirstArg())chojava.lang.ClassCastException: org.mockito.internal.stubbing.answers.ReturnsArgumentAt cannot be cast to
Benedikt Köppel 11/03/2015

1
Lưu ý: returnFirstArg () trả về Trả lời <> thay vì giá trị của đối số. Không thể áp dụng 'Foo (java.lang.String) cho' (org.mockito.stubbing.Answer <java.lang.Object>) 'trong khi cố gắng gọi .thenReturn (Foo mới (returnFirstArg ()))
Lu55

Tôi luôn cần google câu trả lời này nhiều lần trong nhiều năm qua, vì tôi không thể nhớ "ExtraAnswers" và tôi chỉ cần nó rất hiếm khi. Sau đó, tôi tự hỏi làm thế nào tôi có thể xây dựng kịch bản đó khi tôi không thể tìm thấy các phụ thuộc cần thiết. Điều này không thể được thêm trực tiếp vào mockito sao? : /
BAERUS

2
Câu trả lời của Steve thì chung chung hơn. Điều này chỉ cho phép bạn trả về đối số thô. Nếu bạn muốn xử lý lập luận đó và trả về kết quả, thì câu trả lời của Steve là quy tắc. Tôi ủng hộ cả hai vì cả hai đều hữu ích.
akostadinov

FYI, chúng ta phải nhập khẩu static org.mockito.AdditionalAnswers.returnsFirstArg. này để sử dụng returnFirstArg. Ngoài ra, tôi có thể làm when(myMock.myFunction(any())).then(returnsFirstArg())trong Mockito 2.20. *
gtiwari333

77

Với Java 8, có thể tạo câu trả lời một dòng ngay cả với phiên bản cũ hơn của Mockito:

when(myMock.myFunction(anyString()).then(i -> i.getArgumentAt(0, String.class));

Tất nhiên điều này không hữu ích như việc sử dụng được AdditionalAnswersđề xuất bởi David Wallace, nhưng có thể hữu ích nếu bạn muốn chuyển đổi đối số "nhanh chóng".


1
Xuất sắc. Cảm ơn bạn. Nếu tranh luận là long, điều này vẫn có thể làm việc với quyền anh và Long.class?
vikingsteve

1
.getArgumentAt (..) không được tìm thấy cho tôi nhưng .getArgument (1) đã hoạt động (mockito 2.6.2)
Curtis Yallop

41

Tôi đã có một vấn đề rất giống nhau. Mục đích là để chế nhạo một dịch vụ vẫn tồn tại các Đối tượng và có thể trả lại chúng bằng tên của chúng. Dịch vụ này trông như thế này:

public class RoomService {
    public Room findByName(String roomName) {...}
    public void persist(Room room) {...}
}

Dịch vụ giả sử dụng bản đồ để lưu trữ các thể hiện của Phòng.

RoomService roomService = mock(RoomService.class);
final Map<String, Room> roomMap = new HashMap<String, Room>();

// mock for method persist
doAnswer(new Answer<Void>() {
    @Override
    public Void answer(InvocationOnMock invocation) throws Throwable {
        Object[] arguments = invocation.getArguments();
        if (arguments != null && arguments.length > 0 && arguments[0] != null) {
            Room room = (Room) arguments[0];
            roomMap.put(room.getName(), room);
        }
        return null;
    }
}).when(roomService).persist(any(Room.class));

// mock for method findByName
when(roomService.findByName(anyString())).thenAnswer(new Answer<Room>() {
    @Override
    public Room answer(InvocationOnMock invocation) throws Throwable {
        Object[] arguments = invocation.getArguments();
        if (arguments != null && arguments.length > 0 && arguments[0] != null) {
            String key = (String) arguments[0];
            if (roomMap.containsKey(key)) {
                return roomMap.get(key);
            }
        }
        return null;
    }
});

Bây giờ chúng tôi có thể chạy thử nghiệm của chúng tôi trên giả này. Ví dụ:

String name = "room";
Room room = new Room(name);
roomService.persist(room);
assertThat(roomService.findByName(name), equalTo(room));
assertNull(roomService.findByName("none"));

34

Với Java 8, câu trả lời của Steve có thể trở thành

public void testMyFunction() throws Exception {
    Application mock = mock(Application.class);
    when(mock.myFunction(anyString())).thenAnswer(
    invocation -> {
        Object[] args = invocation.getArguments();
        return args[0];
    });

    assertEquals("someString", mock.myFunction("someString"));
    assertEquals("anotherString", mock.myFunction("anotherString"));
}

EDIT: Thậm chí ngắn hơn:

public void testMyFunction() throws Exception {
    Application mock = mock(Application.class);
    when(mock.myFunction(anyString())).thenAnswer(
        invocation -> invocation.getArgument(0));

    assertEquals("someString", mock.myFunction("someString"));
    assertEquals("anotherString", mock.myFunction("anotherString"));
}

6

Đây là một câu hỏi khá cũ nhưng tôi nghĩ vẫn còn liên quan. Ngoài ra câu trả lời được chấp nhận chỉ hoạt động cho String. Trong khi đó, có Mockito 2.1 và một số nhập khẩu đã thay đổi, vì vậy tôi muốn chia sẻ câu trả lời hiện tại của mình:

import static org.mockito.AdditionalAnswers.returnsFirstArg;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

@Mock
private MyClass myClass;

// this will return anything you pass, but it's pretty unrealistic
when(myClass.myFunction(any())).then(returnsFirstArg());
// it is more "life-like" to accept only the right type
when(myClass.myFunction(any(ClassOfArgument.class))).then(returnsFirstArg());

MyClass.myFunction sẽ trông như sau:

public class MyClass {
    public ClassOfArgument myFunction(ClassOfArgument argument){
        return argument;
    }  
}

4

Tôi sử dụng một cái gì đó tương tự (về cơ bản là cách tiếp cận tương tự). Đôi khi thật hữu ích khi có một đối tượng giả trả về đầu ra được xác định trước cho các đầu vào nhất định. Điều đó diễn ra như sau:

private Hashtable<InputObject,  OutputObject> table = new Hashtable<InputObject, OutputObject>();
table.put(input1, ouput1);
table.put(input2, ouput2);

...

when(mockObject.method(any(InputObject.class))).thenAnswer(
       new Answer<OutputObject>()
       {
           @Override
           public OutputObject answer(final InvocationOnMock invocation) throws Throwable
           {
               InputObject input = (InputObject) invocation.getArguments()[0];
               if (table.containsKey(input))
               {
                   return table.get(input);
               }
               else
               {
                   return null; // alternatively, you could throw an exception
               }
           }
       }
       );

4

Bạn có thể muốn sử dụng xác minh () kết hợp với ArgumentCaptor để đảm bảo thực thi trong thử nghiệm và ArgumentCaptor để đánh giá các đối số:

ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);
verify(mock).myFunction(argument.capture());
assertEquals("the expected value here", argument.getValue());

Giá trị của đối số rõ ràng có thể truy cập được thông qua đối số.getValue () để tiếp tục thao tác / kiểm tra / bất cứ điều gì.


3

Điều này hơi cũ, nhưng tôi đến đây vì tôi có cùng một vấn đề. Tôi đang sử dụng JUnit nhưng lần này là trong ứng dụng Kotlin với mockk. Tôi đang đăng một mẫu ở đây để tham khảo và so sánh với đối tác Java:

@Test
fun demo() {
  // mock a sample function
  val aMock: (String) -> (String) = mockk()

  // make it return the same as the argument on every invocation
  every {
    aMock.invoke(any())
  } answers {
    firstArg()
  }

  // test it
  assertEquals("senko", aMock.invoke("senko"))
  assertEquals("senko1", aMock.invoke("senko1"))
  assertNotEquals("not a senko", aMock.invoke("senko"))
}

2

Bạn có thể đạt được điều này bằng cách sử dụng ArgumentCaptor

Hãy tưởng tượng bạn có chức năng đậu như vậy.

public interface Application {
  public String myFunction(String abc);
}

Sau đó, trong lớp kiểm tra của bạn:

//Use ArgumentCaptor to capture the value
ArgumentCaptor<String> param = ArgumentCaptor.forClass(String.class);


when(mock.myFunction(param.capture())).thenAnswer(new Answer<String>() {
    @Override
    public String answer(InvocationOnMock invocation) throws Throwable {
      return param.getValue();//return the captured value.
    }
  });

HOẶC nếu bạn hâm mộ lambda chỉ cần làm:

//Use ArgumentCaptor to capture the value
ArgumentCaptor<String> param = ArgumentCaptor.forClass(String.class);


when(mock.myFunction(param.capture()))
    .thenAnswer((invocation) -> param.getValue());

Tóm tắt: Sử dụng argumentcaptor, để nắm bắt tham số đã truyền. Sau đó trong câu trả lời trả về giá trị được bắt bằng cách sử dụng getValue.


Điều này không làm việc (nữa?). Liên quan đến các tài liệu: Phương pháp này phải được sử dụng bên trong xác minh. Điều đó có nghĩa là bạn chỉ có thể nắm bắt giá trị khi sử dụng phương thức xác minh
Muhammed Misir

1. Không chắc ý của bạn là gì khi This doesn´t work (anymore?).tôi làm việc này. 2. Xin lỗi, tôi không rõ về điểm bạn đang cố gắng thực hiện. Câu trả lời là cụ thể cho câu hỏi của OP.
Cyril Cherian
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.