조롱 된 메소드가 전달 된 인수를 반환하게하십시오.


673

다음과 같은 메소드 서명을 고려하십시오.

public String myFunction(String abc);

Mockito가 메소드가 수신 한 것과 동일한 문자열을 리턴하도록 도울 수 있습니까?


좋아, 일반적인 자바 조롱 프레임 워크는 어떻습니까? 다른 프레임 워크에서 가능합니까? 아니면 원하는 동작을 모방하기 위해 바보 같은 스텁을 만들어야합니까?
Abhijeet Kashnia

답변:


1001

Mockito에서 답변을 만들 수 있습니다. myFunction 메소드가있는 Application이라는 인터페이스가 있다고 가정 해 봅시다.

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

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

Mockito 1.9.5 및 Java 8부터 람다 식을 사용할 수도 있습니다.

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

1
이것도 내가 찾던 것입니다. 감사합니다! 그래도 내 문제는 달랐다. 객체를 저장하고 이름으로 반환하는 지속성 서비스 (EJB)를 조롱하고 싶습니다.
migu

7
답변의 생성을 래핑하는 추가 클래스를 만들었습니다. 코드처럼 읽고 그래서when(...).then(Return.firstParameter())
SpaceTrucker

69
Java 8 람다를 사용하면 특정 클래스, 예를 들어 첫 번째 인수를 반환하는 것이 더 쉽습니다 when(foo(any()).then(i -> i.getArgumentAt(0, Bar.class)). 또한 메소드 참조를 사용하고 실제 메소드를 호출 할 수 있습니다.
Paweł Dyda

이렇게 Iterator<? extends ClassName>하면 thenReturn()명령문 에서 모든 종류의 캐스트 문제를 일으키는 반환 방법으로 문제를 해결할 수 있습니다.
Michael Shopsin

16
자바 8 Mockito로 <1.9.5은 파블의 대답은된다when(foo(any()).thenAnswer(i -> i.getArguments()[0])
그레엄 모스에게

566

Mockito 1.9.5 이상을 사용하는 경우 Answer객체를 만들 수있는 새로운 정적 메서드가 있습니다 . 당신은 뭔가를 작성해야합니다

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

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

또는 대안 적으로

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

returnsFirstArg()메소드는 AdditionalAnswers클래스 에서 정적이며 Mockito 1.9.5에 새로 도입되었습니다. 올바른 정적 가져 오기가 필요합니다.


17
참고 : 그것은 when(...).then(returnsFirstArg())실수로 when(...).thenReturn(returnsFirstArg())줬습니다java.lang.ClassCastException: org.mockito.internal.stubbing.answers.ReturnsArgumentAt cannot be cast to
Benedikt Köppel

1
참고 : returnsFirstArg ()는 인수 값이 아닌 Answer <>을 반환합니다. .thenReturn (new Foo (returnsFirstArg ()))을 호출하는 동안 'Foo (java.lang.String)를'(org.mockito.stubbing.Answer <java.lang.Object>) '에 적용 할 수 없음
Lu55

나는 "AdditionalAnswers"를 기억할 수없고 아주 드물게 필요하기 때문에 지난 몇 년 동안이 답을 몇 번이고 반복해서 검색해야합니다. 그런 다음 필요한 종속성을 찾을 수 없으므로 시나리오를 어떻게 만들 수 있을지 궁금합니다. 이것을 mockito에 직접 추가 할 수는 없습니까? : /
BAERUS

2
스티브의 대답이 더 일반적입니다. 이것은 당신이 원시 인수를 반환 할 수 있습니다. 해당 인수를 처리하고 결과를 반환하려면 Steve의 답변 규칙이 적용됩니다. 둘 다 유용하므로 둘 다 찬성했습니다.
akostadinov

참고로, 우리는 수입해야합니다 static org.mockito.AdditionalAnswers.returnsFirstArg. 이것은 returnsFirstArg를 사용합니다. 또한, when(myMock.myFunction(any())).then(returnsFirstArg())Mockito 2.20에서 가능합니다. *
gtiwari333

77

Java 8을 사용하면 이전 버전의 Mockito에서도 한 줄 답변을 만들 수 있습니다.

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

물론 이것은 AdditionalAnswersDavid Wallace가 제안한 것만 큼 ​​유용 하지는 않지만 "즉시"인수를 변환하려는 경우 유용 할 수 있습니다.


1
훌륭한. 감사합니다. 인수가이면 long여전히 권투와 함께 작동 Long.class합니까?
vikingsteve

1
(mockito 2.6.2) .getArgumentAt (..)는 나를 위해 찾을 수 없습니다하지만 .getArgument (1) 일
커티스 Yallop

41

나는 비슷한 문제가 있었다. 목표는 객체를 유지하고 이름으로 반환 할 수있는 서비스를 조롱하는 것이 었습니다. 서비스는 다음과 같습니다.

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

서비스 모의는 맵을 사용하여 Room 인스턴스를 저장합니다.

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

이제이 모의에서 테스트를 실행할 수 있습니다. 예를 들면 다음과 같습니다.

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

34

Java 8을 사용하면 Steve의 대답 이 될 수 있습니다.

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

편집 : 더 짧음 :

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

이것은 꽤 오래된 질문이지만 여전히 관련이 있다고 생각합니다. 또한 허용되는 답변은 문자열에만 적용됩니다. 한편 Mockito 2.1이 있으며 일부 수입품이 변경되었으므로 현재 답변을 공유하고 싶습니다.

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은 다음과 같습니다.

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

4

나는 비슷한 것을 사용한다 (기본적으로 같은 접근법이다). 때로는 특정 입력에 대해 모의 객체가 미리 정의 된 출력을 반환하도록하는 것이 유용합니다. 이것은 다음과 같습니다.

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

ArgumentCaptor와 함께 verify ()를 사용하여 테스트에서 실행을 보장하고 ArgumentCaptor를 사용하여 인수를 평가할 수 있습니다.

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

인수의 값은 argument.getValue ()를 통해 추가 조작 / 확인 / 무엇이든 액세스 할 수 있습니다.


3

이것은 조금 낡았지만 같은 문제가 있었기 때문에 여기에 왔습니다. JUnit을 사용하고 있지만 이번에는 mockk이있는 Kotlin 앱에서 사용합니다. 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

ArgumentCaptor 를 사용하여이를 달성 할 수 있습니다.

빈 함수가 그렇게 있다고 상상해보십시오.

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

그런 다음 테스트 수업에서

//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.
    }
  });

또는 람다 팬이라면 간단하게 수행하십시오.

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


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

요약 : 전달 된 매개 변수를 캡처하려면 argumentcaptor를 사용하십시오. 나중에 대답은 getValue를 사용하여 캡처 된 값을 반환합니다.


이 작동하지 않습니다 (더 이상?). 문서 관련 :이 방법은 확인 내부에서 사용해야합니다. 즉, verify 메소드를 사용할 때만 값을 캡처 할 수 있습니다
Muhammed Misir

1. This doesn´t work (anymore?).내 인스턴스 에서이 작업을 수행 한다는 것이 무엇을 의미하는지 잘 모르겠습니다 . 2. 죄송합니다, 당신이 만들려고하는 시점에 명확하지 않습니다. 그 대답은 OP의 질문에만 해당됩니다.
시릴 체리 안
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.