mockito when () 호출은 어떻게 작동합니까?


111

다음 Mockito 문이 주어지면 :

when(mock.method()).thenReturn(someValue);

mock.method () 문이 반환 값을 when ()에 전달한다는 점을 감안할 때 Mockito는 mock에 대한 프록시를 생성하는 방법에 대해 설명합니다. 나는 이것이 CGLib 물건을 사용한다고 상상하지만 이것이 기술적으로 어떻게 수행되는지 알고 싶습니다.

답변:


118

짧은 대답은 귀하의 예에서의 결과가 mock.method()유형에 적합한 빈 값이 될 것이라는 것입니다. mockito는 MockingProgressmock에 대한 메서드 호출이 스터 빙에 대한 정보를 반환 값을 통해 전달하는 대신 스터 빙 또는 기존 스터 빙 동작의 재생을위한 것인지 여부를 결정하기 위해 프록시, 메서드 인터 셉션 및 클래스 의 공유 인스턴스를 통한 간접 지정을 사용 합니다. 모의 방법.

mockito 코드를 살펴 보는 몇 분의 미니 분석은 다음과 같습니다. 이것은 매우 대략적인 설명입니다. 여기에 많은 세부 사항이 있습니다. github소스를 직접 확인하는 것이 좋습니다 .

첫째, 클래스의 mock메서드를 사용하여 클래스를 모의하면 다음 과 같은 Mockito일이 발생합니다.

  1. Mockito.mockorg.mockito.internal.MockitoCore.mock에 위임 하여 기본 모의 설정을 매개 변수로 전달합니다.
  2. MockitoCore.mockorg.mockito.internal.util.MockUtil.createMock에 위임
  3. MockUtil클래스는 사용 ClassPathLoader의 예를 얻을 클래스를 MockMaker모의을 만드는 데 사용합니다. 기본적으로 CgLibMockMaker 클래스가 사용됩니다.
  4. CgLibMockMakerClassImposterizer모의 생성을 처리하는 JMock에서 빌린 클래스를 사용합니다 . 사용 된 'mockito 마법'의 주요 부분이있다 MethodInterceptormockito : 모의 만드는 데 사용 MethodInterceptorFilter, 그리고 인스턴스를 포함 MockHandler 인스턴스의 사슬 MockHandlerImpl을 . 메소드 인터셉터는 MockHandlerImpl 인스턴스에 호출을 전달합니다.이 인스턴스는 메소드가 모의 객체에서 호출 될 때 적용되어야하는 비즈니스 로직을 구현합니다 (즉, 응답이 이미 기록되어 있는지 검색, 호출이 새 스텁을 나타내는 지 확인하는 등). 기본 상태는 호출되는 메소드에 대해 스텁이 아직 등록되지 않은 경우 유형에 적합한 값이 리턴되는 것입니다.

이제 예제의 코드를 살펴 보겠습니다.

when(mock.method()).thenReturn(someValue)

이 코드가 실행되는 순서는 다음과 같습니다.

  1. mock.method()
  2. when(<result of step 1>)
  3. <result of step 2>.thenReturn

무슨 일이 일어나고 있는지 이해하는 열쇠는 모의 메서드가 호출 될 때 일어나는 일입니다. 메서드 인터셉터는 메서드 호출에 대한 정보를 전달하고 MockHandler인스턴스 체인에 위임하여 결국 MockHandlerImpl#handle. 동안 MockHandlerImpl#handle, 모의 핸들러의 인스턴스를 생성 OngoingStubbingImpl및 공유에 전달 MockingProgress인스턴스입니다.

when호출 한 후 메서드가 호출 되면 동일한 클래스 의 메서드 를 호출하는에 method()위임됩니다 . 이 메서드 는 모의 호출이 쓴 공유 인스턴스 에서 진행중인 스터 빙을 풀고 이를 반환합니다. 그런 다음 인스턴스 에서 메서드가 호출됩니다 .MockitoCore.whenstub()MockingProgressmethod()thenReturnOngoingStubbing


1
자세한 답변 감사합니다. 또 다른 질문- "method () 호출 후 메소드가 호출 될 때"라고 언급했습니다. when () 호출이 method () 호출을 바로 다음 호출 (또는 래핑)한다는 것을 어떻게 알 수 있습니까? 이해가 되길 바랍니다.
marchaos

@marchaos 몰라요. when(mock.method()).thenXyz(...)구문을 사용하면 mock.method()"스터 빙"모드가 아닌 "재생"모드에서 실행됩니다. 일반적으로, 이러한 실행이 mock.method()때문에 나중에 때 아무런 영향을 미치지 않는다 thenXyz(...)( thenReturn, thenThrow, thenAnswer등)를 실행 얻는다는 모드 "스텁"로 전환하고 그 메소드 호출에 대한 원하는 결과를 기록한다.
Rogério

1
Rogerio, 실제로 그보다 약간 더 미묘합니다. mockito에는 명시적인 스터 빙 및 재생 모드가 없습니다. 나중에 더 명확하게 답변을 편집하겠습니다.
Paul Morie 2013 년

간단히 말해서 "if"연산자와 같이 가로채는 CGLIB 또는 Javassist를 사용하여 다른 메서드 내에서 메서드 호출을 가로채는 것이 더 쉽다고 요약합니다.
Infeligo 2014

나는 아직 여기에 내 설명을 마사지하지 않았지만 그것에 대해서도 잊지 않았다. 참고로.
Paul Morie 2015 년

33

짧은 대답은이면에서 Mockito는 일종의 전역 변수 / 저장소를 사용하여 메서드 스텁 빌드 단계 (예제에서 method (), when (), thenReturn () 호출) 정보를 저장하므로 결국 어떤 매개 변수에서 무엇을 호출 할 때 반환되어야 하는지를지도를 작성합니다.

이 기사가 매우 유용하다는 것을 알았습니다. 프록시 기반 모의 프레임 워크 작동 방식 설명 ( http://blog.rseiler.at/2014/06/explanation-how-proxy-based-mock.html ). 저자는 데모 Mocking 프레임 워크를 구현했으며, 이러한 Mocking 프레임 워크의 작동 방식을 파악하려는 사람들에게 매우 좋은 리소스를 찾았습니다.

제 생각에는 Anti-Pattern의 전형적인 사용법입니다. 일반적으로 메서드를 구현할 때 '부작용'을 피해야합니다. 즉, 메서드가 입력을 받아 계산을 수행하고 결과를 반환해야합니다. 그 외에 다른 변경 사항은 없습니다. 그러나 Mockito는 의도적으로 그 규칙을 위반합니다. 그 메서드는 결과를 반환하는 것 외에도 많은 정보를 저장합니다 : Mockito.anyString (), mockInstance.method (), when (), thenReturn, 모두 특별한 '부작용'이 있습니다. 이것이 프레임 워크가 언뜻보기에 마법처럼 보이는 이유이기도합니다. 우리는 보통 그런 코드를 작성하지 않습니다. 그러나 모의 프레임 워크의 경우이 안티 패턴 디자인은 매우 간단한 API로 이어지기 때문에 훌륭한 디자인입니다.


4
훌륭한 링크. 그 배후의 천재는 다음과 같습니다. 모든 것을 매우 멋지게 만드는 매우 간단한 API입니다. 또 다른 좋은 결정은 when () 메서드가 제네릭을 사용하여 thenReturn () 메서드가 형식에 안전하다는 것입니다.
David Tonhofer

나는 이것이 더 나은 대답이라고 생각합니다. 다른 답변과 달리 구체적인 코드를 통한 제어 흐름 대신 모의 프로세스의 개념을 명확하게 설명합니다. 동의합니다. 훌륭한 링크입니다.
mihca 2010 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.