내부 테스트없이 DI를 사용하는 클래스의 단위 테스트


12

1 개의 메인 클래스와 2 개의 작은 클래스로 리팩토링 된 클래스가 있습니다. 주요 수업은 데이터베이스를 사용하고 (많은 수업과 마찬가지로) 이메일을 보냅니다. 메인 클래스가있다 그래서 IPersonRepository하고는 IEmailRepository자신의 차례에서 2 개 작은 클래스로 보내는 주입.

이제 메인 클래스를 단위 테스트하고 클래스 테스트를 중단하지 않고 내부 작업을 변경할 수 있어야하기 때문에 클래스의 내부 작업을 단위 테스트하지 않는 방법을 배웠습니다.

클래스가 사용하기 때문에 그러나 IPersonRepository하고 IEmailRepository, 나는 가질 에 대한 몇 가지 방법에 대해 (모의 / 더미) 결과를 지정합니다 IPersonRepository. 메인 클래스는 기존 데이터를 기반으로 일부 데이터를 계산하여 반환합니다. 그것을 테스트하고 싶다면 IPersonRepository.GetSavingsByCustomerIdx를 반환 하도록 지정하지 않고 테스트를 작성하는 방법을 알 수 없습니다 . 그러나 내 단위 테스트는 내부 작업에 대해 '알고'있습니다. 왜냐하면 어떤 방법을 조롱해야하는지 알지 못하기 때문입니다.

내부에 대해 테스트하지 않고 종속성을 주입 한 클래스를 어떻게 테스트 할 수 있습니까?

배경:

내 경험상 이와 같은 많은 테스트는 리포지토리에 대한 모의를 만든 다음 모의에 적합한 데이터를 제공하거나 실행 중에 특정 메소드가 호출되었는지 테스트합니다. 어느 쪽이든, 테스트는 내부에 대해 알고 있습니다.

이제 테스트에서 구현에 대해 알아야 할 이론 (이전에 들었음)에 대한 프레젠테이션을 보았습니다. 당신이 테스트되지 않은 먼저 때문에 어떻게 작동하지만, 또한 지금은 구현에 대해 '알고있다'때문에 모든 단위 테스트가 실패 구현을 변경할 때 때문이다. 테스트의 개념이 구현을 모르는 것을 좋아하지만 테스트를 수행하는 방법을 모르겠습니다.


1
메인 클래스가 IPersonRepository객체를 기대하자마자 그 인터페이스와 그 인터페이스가 설명하는 모든 메소드가 더 이상 "내부"가 아니기 때문에 실제로 테스트의 문제는 아닙니다. 당신의 진정한 질문은 "공개적으로 너무 많이 노출시키지 않고 어떻게 수업을 더 작은 단위로 리팩터링 할 수 있는가"여야합니다. 대답은 "예를 들어, 인터페이스 세그먼테이션 원리를 고수하여"인터페이스를 간결하게 유지 "하는 것입니다. @DavidArno의 답변에서 IMHO 포인트 2입니다 (다른 답변에서 반복 할 필요가 없다고 생각합니다).
Doc Brown

답변:


7

메인 클래스는 기존 데이터를 기반으로 일부 데이터를 계산하여 반환합니다. 그것을 테스트하고 싶다면 IPersonRepository.GetSavingsByCustomerIdx를 반환 하도록 지정하지 않고 테스트를 작성하는 방법을 알 수 없습니다 . 그러나 내 단위 테스트는 내부 작업에 대해 '알고'있습니다. 왜냐하면 어떤 방법을 조롱해야하는지 알지 못하기 때문입니다.

이것이 "내부 테스트 안 함"원칙을 위반하는 것이며 사람들이 간과하는 일반적인 원칙입니다.

내부에 대해 테스트하지 않고 종속성을 주입 한 클래스를 어떻게 테스트 할 수 있습니까?

이 위반을 해결하기 위해 채택 할 수있는 두 가지 솔루션이 있습니다.

1) 의 전체 모의를 제공하십시오 IPersonRepository. 현재 설명 된 접근 방식은 호출 할 메소드를 조롱하여 모의를 테스트중인 메소드의 내부 작업에 연결하는 것입니다. 의 모든 방법에 모의를 제공하면 IPersonRepository해당 커플 링을 제거합니다. 내부 작업은 모의에 영향을 미치지 않고 변경 될 수 있으므로 테스트가 덜 취 성화됩니다.

이 방법은 DI 메커니즘을 단순하게 유지하는 이점이 있지만, 인터페이스가 많은 메소드를 정의하면 많은 작업을 생성 할 수 있습니다.

2)IPersonRepository 주입하거나 GetSavingsByCustomerId분석법을 주입하거나 저축 량을 주입 하지 마십시오 . 전체 인터페이스 구현을 주입 할 때 발생하는 문제는 두 가지 접근 방식을 혼합하여 "요청하지 말고"시스템을 주입 ( "말하고 묻지 않음")한다는 점입니다. "순수한 DI"접근 방식을 사용하는 경우, 객체가 제공되지 않고 절약 된 값을 원할 경우 호출 할 정확한 메소드를 제공해야합니다 (효과적으로 호출 할 메소드를 요청해야 함).

이 방법의 장점은 모의 필요성을 피할 수 있다는 것입니다 (테스트중인 방법에 주입하는 테스트 방법 이외). 단점은 메소드 변경 요구 사항에 따라 메소드 서명이 변경 될 수 있다는 것입니다.

두 방법 모두 장단점이 있으므로 필요에 가장 적합한 방법을 선택하십시오.


1
나는 아이디어 번호 2를 정말로 좋아한다. 나는 왜 그런 생각을하지 않았는지 모르겠다. 나는 몇 년 동안 IRepositories에 대한 의존성을 만들어 왔으며, 왜 나는 전체 IRepository (많은 경우 상당히 많은 메소드 서명을 포함 할 것임)에 의존성을 가지고 있지만 하나 또는 2 가지 방법. 따라서 IRepository를 주입하는 대신 필요한 (서식) 메소드 서명을 더 잘 주입하거나 전달할 수 있습니다.
Michel

1

내 접근법은 필요한 데이터가 포함 된 간단한 파일에서 읽는 저장소의 '모의'버전을 만드는 것입니다.

이것은 분명히 전체 테스트 프로젝트가 모의를 참조하고 설정 파일을 가지고 있지만 개별 테스트는 모의 설정에 대해 '알지'않음을 의미합니다.

이를 통해 프레임 워크 모의에 필요한 복잡한 모의 객체 설정을 피할 수 있으며 UI 테스트 등을 위해 실제 응용 프로그램 인스턴스에서 '모의'객체를 사용할 수 있습니다.

'모의'는 테스트 시나리오를 위해 특별히 설정되지 않고 완전히 구현되었으므로 구현 변경으로 인해 GetSavingsForCustomer도 이제 고객을 삭제해야한다고 말할 수 있습니다. 테스트를 중단하지 않을 것입니다 (물론 실제로 테스트를 중단하지 않는 한) 단일 모의 구현 만 업데이트하면 모든 테스트가 설정을 변경하지 않고 실행됩니다.


2
이것은 여전히 ​​테스트 된 클래스가 사용하는 메소드에 대한 데이터로 모의 객체를 만드는 상황으로 이어집니다. 따라서 구현이 변경되면 테스트가 중단되는 문제가 여전히 있습니다.
Michel

1
내 방식이 더 좋은 이유는 아직 시나리오에 완벽하지 않지만, 설명을 편집 난 당신 말은 생각
이완

1

단위 테스트는 일반적으로 화이트 박스 테스트입니다 (실제 코드에 액세스 할 수 있음). 따라서 내부적 인 부분을 어느 정도 알면 괜찮습니다. 그러나 초보자에게는 내부 행동을 테스트해서는 안됩니다 (예 : "방법을 먼저 호출 한 다음, b를 호출 한 다음 다시").

클래스 (단위)가 외부 데이터 (또는 데이터 공급자)에 의존하기 때문에 데이터를 제공하는 모의 객체를 주입하는 것이 좋습니다. 그러나 결과를 확인해야합니다 (결과를 얻는 방법이 아님). 예를 들어 Person 인스턴스 를 제공하고 전자 메일이 올바른 전자 메일 주소로 전송되었는지 확인합니다. 예를 들어 전자 메일에 대한 모의도 제공하면 해당 모의는 나중에 테스트에서 액세스 할 수 있도록 수신자의 전자 메일 주소 만 저장합니다. -암호. (하지만 Martin Fowler는 Mocks 대신 Stubs라고 부릅니다.)

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