인터페이스 사용시기 (단위 테스트, IoC?)


17

나는 여기에 남학생 오류가 있었고 의심을 찾고 있습니다. 내 솔루션 (C #)의 많은 클래스-대다수를 감히 감히-해당 인터페이스를 작성했습니다. 예를 들어 "ICalculator"인터페이스와이를 구현하는 "Calculator"클래스는 해당 계산기를 다른 구현으로 대체 할 가능성은 거의 없습니다. 또한 이러한 클래스의 대부분은 종속성과 동일한 프로젝트에 있습니다. 실제로는 그저 필요 만 internal있지만, public각각의 인터페이스를 구현하는 부작용으로 끝났습니다 .

모든 것에 대한 인터페이스를 만드는이 관행은 몇 가지 허위에서 비롯된 것이라고 생각합니다.

1) 원래 단위 테스트 모의를 작성하는 데 인터페이스가 필요하다고 생각했지만 (Moq를 사용하고 있음) 멤버가 virtual인 경우 클래스를 조롱 할 수 있고 매개 변수가없는 생성자가 있음을 발견했습니다. 내가 틀렸다).

2) 원래 IoC 프레임 워크 (Castle Windsor)에 클래스를 등록하는 데 인터페이스가 필요하다고 생각했습니다.

Container.Register(Component.For<ICalculator>().ImplementedBy<Calculator>()...

실제로 구체적인 유형을 자체에 등록 할 수있을 때 :

Container.Register(Component.For<Calculator>().ImplementedBy<Calculator>()...

3) 인터페이스, 예를 들어 의존성 주입을위한 생성자 파라미터를 사용하면 "느슨한 커플 링"이 발생합니다.

인터페이스에 열광 했습니까?! 공용 API 노출 또는 "플러그 가능"기능과 같은 인터페이스를 "일반적으로"사용하는 시나리오를 알고 있습니다. 내 솔루션에는 그러한 사용 사례에 맞는 소수의 클래스가 있지만 다른 모든 인터페이스가 필요하지 않은지 궁금해 제거해야합니까? 위의 3)과 관련하여 이렇게하면 "느슨한 커플 링"을 위반하지 않습니까?

편집 :-Moq와 함께 놀았으며 메서드를 공개 하고 가상으로 만들고 매개 변수 가없는 공용 생성자가 있어야 모의 할 수 있습니다. 그렇다면 내부 수업을 가질 수없는 것 같습니다.


인터페이스가 공용 인 경우에도 C #에서 인터페이스를 구현하기 위해 클래스를 공용으로 만들 필요는 없다고 확신합니다 ...
vaughandroid

1
비공개 회원은 테스트하지 않아야합니다 . 이것은 신 클래스 반 패턴의 지표로 볼 수 있습니다. 개인 기능을 단위로 테스트해야 할 필요가 있다고 생각되면 해당 기능을 자체 클래스로 캡슐화하는 것이 좋습니다.
Spoike

문제의 프로젝트 @Spoike는 모든 클래스를 내부적으로 만드는 것이 자연스럽게 보였지만 여전히 단위 테스트가 필요한 UI 프로젝트입니까? 내부 메소드를 테스트 할 필요가 없다는 것에 대해 다른 곳을 읽었지만 그 이유를 이해하지 못했습니다.
Andrew Stephens

먼저 테스트중인 장치가 무엇인지 자문하십시오. 전체 수업입니까 아니면 방법입니까? 단위 테스트를 제대로 수행 하려면 해당 단위 를 공개해야합니다 . UI 프로젝트에서는 일반적으로 테스트 가능한 논리를 모두 공용 메소드가있는 컨트롤러 / 뷰 모델 / 서비스로 분리합니다. 실제 뷰 / 위젯은 컨트롤러 / 뷰 모델 / 서비스에 연결되고 정기적 인 연기 테스트를 통해 테스트됩니다 (예 : 앱 시작 및 클릭 시작). 기본 기능을 테스트하고 위젯에서 단위 테스트를 수행하는 시나리오는 취약한 테스트로 이어지며주의해서 수행해야합니다.
Spoike

@Spoike 그래서 당신은 우리의 단위 테스트에 액세스 할 수 있도록 VM 메소드를 공개하고 있습니까? 아마도 내가 잘못 가고있는 곳 일 것입니다. 모든 것을 내부적으로 만들기 위해 열심히 노력했습니다. 나는 내 손이 Moq에 묶여있을 수 있다고 생각합니다. 모크는 클래스를 조롱하기 위해 메소드뿐만 아니라 클래스를 공개해야한다고 생각합니다 (Telastyn의 답변에 대한 내 의견 참조).
Andrew Stephens

답변:


7

일반적으로 구현자가 하나 뿐인 인터페이스를 만들면 두 번만 쓰고 시간을 낭비하게됩니다. 인터페이스만으로는 하나의 구현에 밀접하게 결합 된 경우 느슨한 결합을 제공하지 않습니다. 즉, 단위 테스트에서 이러한 것들을 조롱하려는 경우, 실제로는 실제로 두 개 이상의 구현자가 필요하다는 큰 신호입니다. 암호.

거의 모든 클래스에 인터페이스가 있으면 약간의 코드 냄새가납니다. 그것은 그들이 거의 모든 방식으로 서로 일하고 있다는 것을 의미합니다. 클래스가 너무 많이 수행되고 있거나 (헬퍼 클래스가 없기 때문에) 너무 많이 추상화했다고 생각합니다 (오, 나는 변경 될 수 있기 때문에 중력에 대한 공급자 인터페이스를 원합니다 !).


1
답장을 보내 주셔서 감사합니다. 언급했듯이 대부분의 인터페이스에 구현이 두 개 이상 없다는 것을 알고 있었지만 내가 한 일을 한 몇 가지 잘못된 이유가 있습니다. 문제의 일부는 인터페이스를 조롱하는 데 훌륭한 Moq 프레임 워크를 사용하고 있지만 클래스를 조롱하려면 공개해야하고 매개 변수가없는 ctr이 있어야하며 조롱 할 메소드는 '공개 가상'이어야합니다).
앤드류 스티븐스

단위 테스트 중 대부분의 클래스는 내부 클래스이며 테스트 가능하게 만들기 위해 공개적으로 만들고 싶지 않습니다 (논쟁 할 수는 있지만,이 모든 인터페이스를 작성한 것입니다). 인터페이스를 제거하면 새로운 조롱 프레임 워크를 찾아야 할 것입니다!
Andrew Stephens

1
@AndrewStephens-세상의 모든 것이 조롱 할 필요는 없습니다. 클래스가 인터페이스를 필요로하지 않을 정도로 견고하거나 (혹은 밀접하게 결합 된 경우), 유닛 테스트에서 그대로 사용할 수있을 정도로 견고합니다. 시간 / 복잡성으로 인한 테스트 이점은 없습니다.
Telastyn

-1, 종종 코드를 처음 작성할 때 얼마나 많은 구현자가 필요한지 모릅니다. 처음부터 인터페이스를 사용하는 경우 추가 구현자를 작성할 때 클라이언트를 변경할 필요가 없습니다.
Wilbert

1
@ 윌버 트-작은 인터페이스는 클래스보다 더 읽기 쉽지만 하나 또는 다른 것을 선택하지는 않습니다. 클래스 또는 클래스 인터페이스가 있습니다. 그리고 당신은 내 대답을 너무 많이 읽고있을 것입니다. 나는 결코 단일 구현을위한 인터페이스를 만들지 않는다고 말하지는 않는다 . 실제로 대부분의 상호 작용에는 유연성이 필요하기 때문에 대부분의 경우에해야한다. 그러나 질문을 다시 읽으십시오. 모든 것을 위한 인터페이스를 만드는 것은 단지화물 배양입니다. IoC는 이유가 아닙니다. Mocks는 이유가 아닙니다. 요구 사항은 이러한 유연성을 구현하는 이유입니다.
Telastyn

4

1) 구체적인 클래스를 조롱 할 수는 있지만 여전히 멤버를 만들고 virtual매개 변수가없는 생성자를 제공해야합니다. 당신 (또는 새로운 팀원)은 곧 virtual"새로운 방식으로 모든 것이 작동하는 방식"이기 때문에 모든 새로운 클래스에서 '와 매개 변수가없는 생성자를 체계적으로 추가한다는 것을 알게 될 것 입니다.

인터페이스는 실제로 필요할 때까지 구현을 연기하고 종속성을 명확하게 축소시킬 수 있기 때문에 종속성을 조롱해야 할 때 IMO가 훨씬 더 좋습니다.

3) 그것은 어떻게 거짓입니까?

인터페이스가 공동 작업 객체 간의 메시징 프로토콜을 정의하는 데 적합하다고 생각합니다. 예 를 들어 도메인 엔터티 와 같이 필요가있을 수있는 경우가있을 수 있습니다 . 그러나 통신해야하는 안정적인 파트너 (예 : 일시적 참조가 아닌 주입 된 종속성)가있는 경우 일반적으로 인터페이스 사용을 고려해야합니다.


3

IoC의 아이디어는 콘크리트 유형을 교체 할 수 있도록하는 것입니다. 따라서 (현재) ICalculator를 구현하는 단일 계산기 만있는 경우에도 두 번째 구현은 인터페이스에만 의존 할 수 있으며 Calculator의 구현 세부 정보에는 의존하지 않을 수 있습니다.

따라서 IoC-Container 등록의 첫 번째 버전은 올바른 것이며 나중에 사용해야합니다.

수업을 공개 또는 내부로 만들면 실제로 관련이 없으며 moq-ing도 아닙니다.


2

전체 프로젝트에서 거의 모든 유형을 "모의"해야 할 필요성을 느끼고 있다는 사실이 더 걱정됩니다.

테스트 복식은 외부 세계 (타사 라이브러리, 디스크 IO, 네트워크 IO 등) 또는 실제로 고가의 계산에서 코드를 분리하는 데 주로 유용합니다. 격리 프레임 워크가 너무 자유롭기 때문에 (실제로 필요합니까? 복잡한 프로젝트입니까?) 구현에 연결된 테스트로 쉽게 이어질 수 있으며 디자인이 더욱 엄격 해집니다. 메소드 X와 Y라는 클래스가 순서대로 있는지 확인한 다음 매개 변수 a, b 및 c를 메소드 Z에 전달했는지 테스트하는 많은 테스트를 작성하면 실제로 값을 얻습니까? 로깅 또는 타사 라이브러리와의 상호 작용을 테스트 할 때 이와 같은 테스트를받는 경우도 있지만 규칙이 아닌 예외 여야합니다.

격리 프레임 워크에서 물건을 공개해야하고 항상 매개 변수가없는 생성자를 가져야한다고 알려주는 즉시,이 시점에서 프레임 워크가 잘못된 (더 나쁜) 코드를 작성해야하기 때문에 피해야 할 시점을 피할 차례입니다.

인터페이스에 대해 더 구체적으로 말하면 몇 가지 주요 경우에 유용합니다.

  • 예를 들어 여러 구현이있을 때 메소드 호출을 다른 유형으로 디스패치해야하는 경우 (대부분의 언어에서 인터페이스 정의는 가상 메소드 콜렉션이므로)

  • 나머지 코드를 일부 클래스와 분리해야 할 때. 이것은 파일 시스템, 네트워크 액세스 및 데이터베이스 등을 응용 프로그램의 핵심 논리에서 추상화하는 일반적인 방법입니다.

  • 어플리케이션 경계를 보호하기 위해 제어 역전이 필요한 경우. 이것은 종속성을 관리 할 수있는 가장 강력한 방법 중 하나입니다. 우리는 모두 높은 수준의 모듈과 낮은 수준의 모듈이 서로 다른 이유에 대해 알지 않아야한다는 것을 알고 있습니다. 왜냐하면 서로 다른 이유로 변경 될 것이므로 도메인 모델의 HttpContext 또는 행렬 곱셈 라이브러리의 일부 PDF 렌더링 프레임 워크에 의존하지 않기를 바랍니다. . 바운더리 클래스가 인터페이스를 구현하지 않으면 (교체되지 않더라도) 계층 간 연결이 느슨해 지므로 너무 많은 다른 유형을 알고있는 유형에서 계층을 통해 종속성이 발생할 위험이 크게 줄어 듭니다.


1

나는 어떤 논리가 사적이라면, 그 논리의 구현은 누구에게도 문제가되지 않아야하며, 따라서 테스트되지 않아야한다는 생각에 기대어있다. 일부 로직이 테스트하기에 충분히 복잡하면 해당 로직은 분명한 역할을 수행 할 수 있으므로 공개되어야합니다. 예를 들어 개인 도우미 메서드에서 코드를 추출하면 일반적으로 별도의 공개 클래스 (포매터, 파서, 매퍼 등)에있을 수있는 것입니다.
인터페이스에 관해서는 수업을 스텁하거나 조롱해야 할 필요성을 느끼게했습니다. repos와 같은 것들, 나는 보통 스텁하거나 조롱 할 수 있기를 원합니다. 통합 테스트의 매퍼 (일반적으로)

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