Mockito.verify ()를 언제 사용합니까?


201

3 가지 목적으로 jUnit 테스트 사례를 작성합니다.

  1. 내 코드가 모든 (또는 대부분의) 입력 조합 / 값 하에서 필요한 기능을 모두 충족 시키려면.
  2. 구현을 변경하고 JUnit 테스트 사례를 사용하여 모든 기능이 여전히 만족 스럽다는 것을 알려줍니다.
  3. 코드를 다시 작성해야하는 경우 내 코드가 처리하고 리팩토링 사양으로 작동하는 모든 사용 사례에 대한 문서입니다. (코드를 리팩터링하고 jUnit 테스트가 실패하면 유스 케이스를 놓친 것 같습니다).

왜 또는 언제 Mockito.verify()사용해야하는지 이해하지 못합니다 . verify()호출 된 것을 볼 때 jUnit이 구현을 인식하고 있음을 알려줍니다. (따라서 내 기능이 영향을받지 않더라도 구현을 변경하면 jUnit이 손상됩니다).

내가 찾고 있어요:

  1. 적절한 사용법에 대한 지침은 무엇입니까 Mockito.verify()?

  2. jUnit이 테스트중인 클래스의 구현을 인식하거나 밀접하게 결합하는 것이 근본적으로 정확합니까?


1
나는 당신이 노출 한 것과 같은 이유로 (가능한 한 내 단위 테스트가 구현을 알고 싶지 않기 때문에) verify () 사용을 피하려고 노력하지만 선택의 여지가없는 경우가 있습니다. -스터드 보이드 방법. 일반적으로 그들이 '실제'출력에 기여하지 않는 것을 반환하지 않기 때문에 말하기; 그러나 여전히 호출되었다는 것을 알아야합니다. 그러나 나는 실행 흐름을 확인하기 위해 verify를 사용하는 것이 의미가 없다는 것에 동의합니다.
Legna 2016 년

답변:


78

클래스 A의 계약에 유형 C의 오브젝트의 메소드 B를 호출한다는 사실이 포함 된 경우, 유형 C의 모형을 작성하고 메소드 B가 호출되었는지 확인하여이를 테스트해야합니다.

이는 클래스 A의 계약에 유형 C (인터페이스 또는 클래스 일 수 있음)에 대해 설명하는 충분한 세부 사항이 있음을 의미합니다. 그렇습니다. 우리는 단지 "시스템 요구 사항"을 넘어서서 구현을 설명하는 어떤 수준의 사양에 대해 이야기하고 있습니다.

이것은 단위 테스트에서 정상입니다. 단위 테스트를 수행 할 때 각 단위가 "올바른 일"을하고 있는지 확인하고 다른 단위와의 상호 작용을 포함해야합니다. 여기서 "단위"는 클래스 또는 응용 프로그램의 더 큰 하위 집합을 의미 할 수 있습니다.

최신 정보:

나는 이것이 검증에만 적용되는 것이 아니라 스터 빙에도 적용된다고 생각합니다. 공동 작업자 클래스의 메소드를 스텁하자마자 단위 테스트는 구현에 따라 일부 의미가 있습니다. 그것은 단위 테스트의 성격 상 그러합니다. Mockito는 검증만큼이나 스터 빙에 관한 것이기 때문에 Mockito를 전혀 사용한다는 사실은 이러한 종류의 의존성을 극복해야한다는 것을 의미합니다.

경험상 클래스 구현을 변경하면 단위 테스트 구현을 일치하도록 변경해야하는 경우가 종종 있습니다. 일반적으로,하지만, 나는 거기에 어떤 단위 테스트의 재고 변경할 필요가 없습니다 것 입니다 클래스에 대해를; 물론이 아닌 한, 변경 이유는 내가 이전에 테스트하지 못한 상태가 존재했기 때문입니다.

이것이 단위 테스트에 관한 것입니다. 협력자 클래스가 사용되는 방식에 대한 이러한 종류의 종속성이없는 테스트는 실제로 서브 시스템 테스트 또는 통합 테스트입니다. 물론 이것들은 종종 JUnit으로 작성되며 종종 모의 사용을 포함합니다. 제 생각에 "JUnit"은 모든 다른 유형의 테스트를 생성 할 수있는 제품의 끔찍한 이름입니다.


8
고마워, 데이빗 일부 코드 세트를 스캔 한 후 이것은 일반적인 관행처럼 보입니다. 그러나 이것은 단위 테스트 작성의 목적을 상실하고 매우 적은 가치로 유지 관리하는 오버 헤드를 추가합니다. 모의가 필요한 이유와 테스트 실행을위한 종속성을 설정해야하는 이유를 이해합니다. 그러나 메소드 종속성 A.XYZ ()가 실행되는지 확인하면 테스트가 매우 취하기 쉽습니다.
Russell

@Russell "type C"가 라이브러리 또는 응용 프로그램의 고유 한 하위 시스템 주위의 래퍼에 대한 인터페이스 인 경우에도?
Dawood ibn Kareem

1
나는 일부 서브 시스템이나 서비스가 호출되도록하는 것이 완전히 쓸모가 없다고 말하지 않을 것입니다. 예를 들어 : (아마도 지나치게 단순합니다) 예, 코드에서 StrUtil.equals ()를 사용하고 있으며 구현에서 StrUtil.equalsIgnoreCase ()로 전환하기로 결정합니다. ), 구현이 정확하지만 테스트가 실패 할 수 있습니다. 이 확인 호출 IMO는 라이브러리 / 하위 시스템 용이지만 잘못된 방법입니다. 반면, verifyD를 사용하여 closeDbConn에 대한 호출이 유효한 유스 케이스가되도록하십시오.
Russell

1
나는 당신을 이해하고 당신에게 완전히 동의합니다. 그러나 또한 귀하가 설명하는 지침을 작성하면 전체 TDD 또는 BDD 교과서 작성으로 확장 될 수 있다고 생각합니다. 전화를 equals()받거나 equalsIgnoreCase()클래스의 요구 사항에 지정된 것이 아니기 때문에 단위 테스트를하지 마십시오. 그러나 "완료되면 DB 연결 닫기"(구현 측면에서 무엇이든)는 "비즈니스 요구 사항"이 아니더라도 클래스의 요구 사항 일 수 있습니다. 나를 위해, 이것은 계약 간의 관계에 온다 ...
Dawood ibn Kareem

... 비즈니스 요구 사항에 명시된 클래스 및 해당 클래스를 단위로 테스트하는 일련의 테스트 방법. 이 관계를 정의하는 것은 TDD 또는 BDD에 대한 모든 책에서 중요한 주제입니다. Mockito 팀의 누군가가이 주제에 대해 Wiki에 대한 게시물을 쓸 수는 있지만, 다른 많은 문헌과 어떻게 다른지 알 수 없습니다. 그것이 어떻게 다른지 알면 알려주십시오. 어쩌면 우리가 함께 할 수 있습니다.
Dawood ibn Kareem

60

David의 대답은 물론 정확하지만 왜 이것을 원할지는 잘 설명하지 못합니다.

기본적으로 단위 테스트시 기능 단위를 개별적으로 테스트합니다. 입력이 예상 출력을 생성하는지 테스트합니다. 때로는 부작용도 테스트해야 할 때가 있습니다. 간단히 말해 확인하면 그렇게 할 수 있습니다.

예를 들어 DAO를 사용하여 물건을 저장 해야하는 약간의 비즈니스 논리가 있습니다. 통합 테스트를 사용하여 DAO를 인스턴스화하고이를 비즈니스 로직에 연결 한 다음 데이터베이스에서 펑크하여 예상되는 항목이 저장되었는지 확인할 수 있습니다. 그것은 더 이상 단위 테스트가 아닙니다.

또는 DAO를 조롱하여 예상대로 호출되는지 확인할 수 있습니다. mockito를 사용하면 무언가가 호출되는지, 얼마나 자주 호출되는지 확인하고, 매개 변수에 대해 매처를 사용하여 특정 방식으로 호출되도록 할 수 있습니다.

이와 같은 단위 테스트의 반대 측면은 실제로 리팩토링을 조금 더 어렵게 만드는 구현에 테스트를 묶는 것입니다. 다른 한편으로, 좋은 디자인 냄새는 제대로 운동하는 데 필요한 코드의 양입니다. 테스트가 매우 길어야하는 경우 설계에 문제가있을 수 있습니다. 따라서 테스트해야 할 부작용 / 복잡한 상호 작용이 많은 코드는 바람직하지 않습니다.


29

이것은 좋은 질문입니다! 근본 원인은 다음과 같습니다. 단위 테스트뿐만 아니라 JUnit을 사용하고 있습니다. 따라서 질문은 분리되어야합니다.

  • 통합 (또는 다른 단위 이상의 테스트) 테스트 에서 Mockito.verify ()를 사용해야 합니까?
  • 블랙 박스 단위 테스트 에서 Mockito.verify ()를 사용해야 합니까?
  • 화이트 박스 단위 테스트 에서 Mockito.verify ()를 사용해야 합니까?

만약 우리가 단위보다 높은 테스트를 무시한다면, " Mockito.verify ()와 함께 화이트 박스 단위 테스트를 사용하면 단위 테스트와 가능한 구현 사이에 훌륭한 커플을 만들 수 있습니다. " " 단위 테스트와 이것에 어떤 규칙을 사용해야합니까 ".

이제이 모든 단계를 단계별로 살펴 보겠습니다.

*- 통합 (또는 다른 단위보다 높은 다른 테스트) 테스트 에서 Mockito.verify ()를 사용해야 합니까? * 대답은 분명히 아니오라고 생각합니다. 또한이를 위해 모의를 사용해서는 안됩니다. 테스트는 가능한 한 실제 응용 프로그램에 가깝습니다. 애플리케이션의 분리 된 부분이 아닌 완전한 유스 케이스를 테스트하고 있습니다.

* 블랙 박스화이트 박스 단위 테스트 * 실제로 수행중인 블랙 박스 방식을 사용하는 경우 (모든 동등성 클래스) 입력, 상태 및 예상 출력을받을 테스트를 제공합니다. 이 접근법에서 모의 ​​사용은 일반적으로 정당화됩니다 (당신은 그들이 옳은 일을하고 있음을 모방하고 테스트하고 싶지는 않습니다). 그러나 Mockito.verify () 호출은 불필요한 것입니다.

실제 작업 을 화이트 박스 방식 으로 사용하는 경우 장치 의 동작 을 테스트하는 것 입니다. 이 접근 방식에서는 Mockito.verify ()를 호출하는 것이 필수적이므로 장치가 예상대로 작동하는지 확인해야합니다.

그레이 박스 테스트를위한 엄지 손가락 규칙 화이트 박스 테스트의 문제점은 높은 커플 링을 생성한다는 것입니다. 하나의 가능한 해결책은 화이트 박스 테스트가 아닌 그레이 박스 테스트를 수행하는 것입니다. 이것은 흑백 박스 테스트의 조합입니다. 화이트 박스 테스트와 같이 실제로 장치 의 동작 을 테스트하고 있지만 일반적으로 가능하면 구현에 무관 하게 만듭니다 . 가능하면 블랙 박스와 같이 검사를 수행하고 출력이 예상되는 것임을 주장합니다. 따라서 질문의 본질은 가능할 때입니다.

정말 어렵다. 좋은 예는 없지만 예를들 수 있습니다. 위에서 equals () vs equalsIgnoreCase ()로 언급 한 경우 Mockito.verify ()를 호출하지 말고 출력을 확인하십시오. 그렇게 할 수 없다면 코드를 더 작은 단위로 나누십시오. 반면에 @Service가 있고 @Service를 래퍼하는 @ Web-Service를 작성한다고 가정하면 모든 호출을 @Service에 위임하고 추가 오류 처리를 수행합니다. 이 경우 Mockito.verify ()를 호출하는 것이 필수적이므로 @Serive에 대해 수행 한 모든 검사를 복제하지 않아야합니다. 올바른 매개 변수 목록으로 @Service를 호출하는 것이 충분한 지 확인하십시오.


그레이 박스 테스트는 약간의 함정입니다. 나는 그것을 DAO와 같은 것들로 제한하는 경향이 있습니다. 그레이 박스 테스트가 풍부하고, 단위 테스트가 거의없고, 블랙 박스 테스트가 너무 많아서 그레이 박스 테스트가 테스트 할 것으로 예상되는 것에 대한 신뢰 부족을 보상하기 때문에 빌드가 매우 느린 일부 프로젝트에 참여했습니다.
Jilles van Gurp 2016 년

나에게 이것은 다양한 상황에서 Mockito.when ()을 사용할 때 대답하기 때문에 가장 유용한 대답입니다. 잘 했어.
Michiel Leegwater 5

8

나는 당신이 고전적인 접근법의 관점에서 절대적으로 옳다고 말해야합니다.

  • 애플리케이션의 비즈니스 로직 을 먼저 생성 (또는 변경) 한 다음 (적용) 테스트 ( 테스트-마지막 접근법 ) 로 다루는 경우 , 테스트가 아닌 다른 방식으로 소프트웨어 작동 방식에 대해 테스트에 알리는 것은 매우 고통스럽고 위험합니다. 입력 및 출력 확인.
  • Test-Driven 방식을 연습하는 경우 테스트가 먼저 작성되고 변경되고 소프트웨어 기능 의 사용 사례가 반영 됩니다. 구현은 테스트에 따라 다릅니다. 즉, 소프트웨어가 특정 방식으로 구현되기를 원한다는 의미입니다. 예를 들어 다른 구성 요소의 방법에 의존하거나 특정 횟수만큼 호출하기도합니다. 바로 Mockito.verify () 가 편리합니다!

보편적 인 도구가 없다는 것을 기억하는 것이 중요합니다. 소프트웨어 유형, 규모, 회사 목표 및 시장 상황, 팀 기술 및 기타 여러 가지 사항이 특정 사례에서 어떤 접근 방식을 사용할지 결정하는 데 영향을줍니다.


0

일부 사람들이 말했듯이

  1. 때때로 당신은 당신이 주장 할 수있는 직접 출력이 없습니다
  2. 때로는 테스트 된 메소드가 올바른 간접 출력을 협력자 (모의중인)에게 보내는 지 확인해야합니다.

리팩토링시 테스트 중단에 대한 우려와 관련하여 모의 / 스텁 / 스파이를 사용할 때 다소 예상됩니다. 나는 정의상 Mockito와 같은 특정 구현과 관련이 없다는 것을 의미합니다. 그러나이 방법으로 생각할 수있는 - 당신이하는 방식에 큰 변화를 만들 것 리팩토링을해야 할 경우 방법 작품, 그것은 TDD 방식에 그것을 할 수있는 좋은 생각이 당신이 당신의 테스트를 변경할 수 있습니다 의미입니다 첫번째 을 정의 할 수 새로운 동작 (테스트에 실패 할 것)을 변경 한 다음 변경 수행하고 테스트를 다시 통과시킵니다.


0

대부분의 경우 사람들이 Mockito.verify 사용을 좋아하지 않는 경우 테스트 된 장치가 수행하는 모든 작업을 확인하는 데 사용되므로 변경 사항이 있으면 테스트를 조정해야합니다. 그러나 나는 그것이 문제라고 생각하지 않습니다. 테스트를 변경할 필요없이 메소드가 수행하는 작업을 변경하려면 기본적으로 메소드가 수행하는 모든 것을 테스트하지 않는 테스트를 작성해야 함을 의미합니다. 변경을 테스트하기를 원하지 않기 때문입니다. . 그리고 그것은 잘못된 사고 방식입니다.

실제로 문제가되는 것은 메소드의 기능을 수정할 수 있고 기능을 완전히 다루어야하는 단위 테스트가 실패하지 않는 것입니다. 변경의 의도가 무엇이든 변경의 결과는 테스트에 포함되지 않습니다.

그 때문에 가능한 한 조롱하고 데이터 개체를 조롱하는 것을 선호합니다. 그렇게하면 verify를 사용하여 다른 클래스의 올바른 메소드가 호출되는지 확인할 수있을뿐만 아니라 전달되는 데이터가 해당 데이터 오브젝트의 올바른 메소드를 통해 수집되는지 확인할 수 있습니다. 그리고 완료하려면 호출이 발생하는 순서를 테스트해야합니다. 예 : db 엔티티 오브젝트를 수정 한 후 저장소를 사용하여 저장하는 경우, 오브젝트의 setter가 올바른 데이터로 호출되고 저장소의 저장 메소드가 호출되는지 확인하는 것만으로는 충분하지 않습니다. 그것들이 잘못된 순서로 호출 된 경우, 메소드는 여전히해야 할 일을하지 않습니다. 따라서 Mockito.verify를 사용하지 않지만 모든 모의 객체로 inOrder 객체를 만들고 대신 inOrder.verify를 사용합니다. 그리고 그것을 완성시키고 싶다면 Mockito에게도 전화해야합니다. 끝에 NoNoInteractions을 확인하고 모든 모의를 전달하십시오. 그렇지 않으면 누군가가 테스트하지 않고 새로운 기능 / 동작을 추가 할 수 있습니다. 이는 커버리지 통계가 100 %가 될 수 있지만 여전히 주장되거나 확인되지 않은 코드를 쌓고 있음을 의미합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.