[unit-testing] mockito when () 호출은 어떻게 작동합니까?

다음 Mockito 문이 주어지면 :

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

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



답변

짧은 대답은 귀하의 예에서의 결과가 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


답변

짧은 대답은이면에서 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로 이어지기 때문에 훌륭한 디자인입니다.


답변