테스트 가능한 코드 작성과 추론 적 일반성 피하기


11

오늘 아침에 일부 블로그 게시물을 읽고, 그리고 운 좋게 발견 한 이것 :

Customer 인터페이스를 구현하는 유일한 클래스가 CustomerImpl 인 경우 실제로는 런타임에 대체 할 것이 없기 때문에 다형성 및 대체 가능성이 없습니다. 가짜 일반성입니다.

인터페이스를 구현하면 복잡성이 추가되고 구현이 하나 뿐인 경우 불필요한 복잡성을 추가한다고 주장 할 수 있습니다. 필요한 것보다 더 추상적 인 코드를 작성하는 것은 종종 "추론 일반성"(포스트에서 언급 됨)이라는 코드 냄새로 간주됩니다.

그러나 TDD를 따르는 경우 인터페이스 구현 또는 기타 다형성 옵션의 형태로 클래스를 상속 가능하고 메소드를 가상으로 만들지 여부에 따라 투기 일반성이 없으면 테스트 배가를 쉽게 만들 수 없습니다.

그렇다면 우리는이 트레이드 오프를 어떻게 조정합니까? 테스트 / TDD를 용이하게하기 위해 추론 적으로 일반적인 것이 가치가 있습니까? 테스트 복식을 사용하는 경우 두 번째 구현으로 간주되어 일반성이 더 이상 투기 적이 지 않습니까? 구체적인 공동 작업자 (예 : C # 세계에서 Moles vs Moq)를 조롱 할 수있는보다 무거운 조롱 프레임 워크를 고려해야합니까? 또는 구체적인 클래스로 테스트하고 디자인에 자연스럽게 다형성이 필요할 때까지 "통합"테스트로 간주 할 수있는 것을 작성해야합니까?

귀하의 의견에 미리 감사드립니다.


개인적으로 나는 실체를 조롱해서는 안된다고 생각합니다. 코드 도메인 코드에는 일반적으로 서비스가 구현되는 코드에 대한 참조가 없기 때문에 서비스 만 조롱하며 어떤 경우에도 인터페이스가 필요합니다.
코드 InChaos

7
우리는 동적으로 입력 된 언어를 사용하는 사용자가 정적으로 입력 한 언어가 사용하는 체인을보고 웃습니다. 이것은 동적으로 유형이 지정된 언어에서 단위 테스트를 쉽게하는 것 중 하나입니다. 테스트 목적으로 객체를 대체하기 위해 인터페이스를 개발할 필요가 없습니다.
Winston Ewert

인터페이스는 일반성에 영향을 줄뿐만 아니라 사용됩니다. 그것들은 많은 목적으로 사용되며, 코드의 분리는 중요한 코드 중 하나입니다. 결과적으로 코드 테스트가 훨씬 쉬워집니다.
Marjan Venema

@WinstonEwert 이것은 동적 타이핑의 흥미로운 이점인데, 이전에 지적한 것처럼 동적 타이핑 언어에서 작동하지 않는 사람으로 생각한 적이 있습니다.
Erik Dietrich

@CodeInChaos 나는이 질문의 목적을 위해 구별을 고려하지 않았지만, 만드는 것은 합리적인 구별입니다. 이 경우, 하나의 (현재) 구현으로 서비스 / 프레임 워크 클래스를 생각할 수 있습니다. DAO로 액세스 할 수있는 데이터베이스가 있다고 가정 해 봅시다. 보조 지속성 모델이있을 때까지 DAO를 인터페이스하지 않아야합니까? (이것은 블로그 포스트 작성자가 암시하는 것 같습니다)
Erik Dietrich

답변:


14

나는 가서 블로그 게시물을 읽고 저자의 말에 동의합니다. 당신이 단위 테스트를 위해 인터페이스를 사용하여 코드를 작성하는 경우, 나는 인터페이스의 모의 구현은 말할 것 입니다 두 번째 구현입니다. 특히 코드를 복잡하게 만들지 않는 이유는 무엇입니까? 특히 그렇게하지 않으면 클래스가 밀접하게 결합되어 테스트하기가 어려울 경우 더욱 그렇습니다.


3
확실히 맞아. 테스트 코드 디자인, 구현, 유지 관리 등을 올바르게 수행하기 위해 필요하기 때문에 응용 프로그램의 일부입니다. 고객에게 제공하지 않는다는 사실은 관련이 없습니다. 테스트 스위트에 두 번째 구현이있는 경우 일반성 있으며이를 수용해야합니다.
Kilian Foth

1
이것이 내가 가장 설득력이있는 답입니다 (그리고 코드가 두 번째 구현을 제공하는지 여부를 @KilianFoth가 덧붙여서 덧붙입니다). 그래도 누군가 다른 사람이 들어오는 지 알기 위해 대답을 조금 받아 들일 것입니다.
Erik Dietrich 17

나는 또한 당신이 시험의 인터페이스에 따라 때, 일반성이 더 이상 투기 적이며, 추가하지 않을 것이다
피트

"그렇지 않으면"(인터페이스 사용) 클래스가 밀접하게 연결되지 않습니다. 그렇지 않습니다. 예를 들어 .NET Framework에는 Stream클래스가 있지만 타이트 커플 링은 없습니다.
Luke Puplett

3

일반적으로 코드 테스트는 쉽지 않습니다. 만약 그렇다면, 우리는 오래 전에 그것을 해왔으며, 지난 10-15 년 동안 만 그렇게 많은 거래를하지 않았을 것입니다. 가장 큰 어려움 중 하나는 캡슐화를 깨지 않고 응집력 있고 잘 팩토링되고 테스트 가능한 코드를 테스트하는 방법을 결정하는 데 항상 어려움이있었습니다. BDD 교장은 우리가 행동에 거의 전적으로 초점을 맞출 것을 제안하고, 어떤면에서는 내부 세부 사항에 대해 크게 걱정할 필요가 없다고 제안하는 것처럼 보이지만 종종 이것이 있다면 테스트하기가 매우 어려울 수 있습니다 테스트의 전체적인 복잡성을 증가 시켜서 모든 가능한 결과를보다 공공 수준에서 처리 할 수 ​​있기 때문에 매우 숨겨진 방식으로 "물건"을 수행하는 많은 개인용 메소드.

조롱은 어느 정도 도움이 될 수 있지만 다시 외부 적으로 집중되어 있습니다. Dependency Injection은 모의 또는 테스트 복식으로 다시 잘 작동 할 수 있지만 인터페이스를 통해 또는 직접 숨기고 선호하는 요소를 직접 노출해야 할 수도 있습니다. 시스템 내 특정 클래스에 대한 편집증 수준의 보안을 유지하십시오.

나를 위해, 배심원은 클래스를 더 쉽게 테스트 할 수 있도록 디자인할지 여부에 대해 여전히 고민 중입니다. 레거시 코드를 유지하면서 새로운 테스트를 제공해야하는 경우 문제가 발생할 수 있습니다. 나는 당신이 시스템의 모든 것을 절대적으로 테스트 할 수 있어야한다는 것을 인정하지만, 클래스의 개인 내부를 간접적으로 노출시키는 아이디어는 마음에 들지 않으므로 테스트를 작성할 수 있습니다.

나에게 해결책은 항상 상당히 실용적인 접근 방식을 취하고 각 특정 상황에 맞게 여러 가지 기술을 결합하는 것이 었습니다. 내 테스트의 내부 속성과 동작을 노출시키기 위해 상속 된 많은 테스트 복식을 사용합니다. 수업에 첨부 할 수있는 모든 것을 조롱하고 수업의 보안을 훼손하지 않는 경우 테스트 목적으로 행동을 무시하거나 주입하는 방법을 제공합니다. 코드 테스트 기능을 향상시키는 데 도움이된다면 더 이벤트 중심 인터페이스를 제공하는 것을 고려할 것입니다.

"가장 알아볼 수없는" 코드를 발견하면 리팩토링을 통해 테스트 할 수 있는지 확인합니다. 씬 뒤에 숨어있는 많은 개인 코드가있는 경우 종종 새로운 클래스가 깨지기를 기다리고 있습니다. 이러한 클래스는 내부적으로 사용될 수 있지만 개인 행동이 적고 액세스 및 복잡성이 낮은 계층으로 독립적으로 테스트 될 수 있습니다. 그러나 피해야 할 한 가지 사항은 테스트 코드가 내장 된 프로덕션 코드를 작성하는 것입니다. 테스트 공포가 if testing then ...완전히 해체되지 않고 불완전하게 해결되지 않았 음을 나타내는 " 러그 " 를 생성하는 유혹이있을 수 있습니다 .

Gerard Meszaros의 xUnit Test Patterns 책 을 읽는 것이 도움이 될 것 입니다.이 책은 내가 여기에 갈 수있는 것보다 훨씬 더 자세한 내용을 다룹니다. 아마 그가 제안한 모든 것을하지는 않겠지 만, 다루기 어려운 까다로운 테스트 상황을 명확히하는 데 도움이됩니다. 하루가 끝나면 선호하는 디자인을 적용하면서 테스트 요구 사항을 충족 할 수 있기를 원하며 타협해야 할 위치를 더 잘 결정하기 위해 모든 옵션을 더 잘 이해하는 데 도움이됩니다.


1

사용하는 언어에 테스트를 위해 개체를 "모의"할 수있는 방법이 있습니까? 그렇다면 이러한 성가신 인터페이스는 사라질 수 있습니다.

다른 메모에는 SimpleInterface와이를 구현하는 유일한 ComplexThing이있는 이유가있을 수 있습니다. SimpleInterface 사용자가 액세스하지 않으려는 ComplexThing 조각이있을 수 있습니다. 과도하게 풍부한 OO-ish 코더 때문에 항상 그런 것은 아닙니다.

나는 이제 물러서서 이것을하는 코드가 그들에게 "나쁜 냄새를 맡는다"는 사실에 뛰어 들게 할 것이다.


예, 콘크리트 객체를 조롱하는 것을 지원하는 조롱 프레임 워크를 사용하여 언어로 작업합니다. 이러한 도구를 사용하려면 굴렁쇠를 통해 다양한 각도로 점프해야합니다.
Erik Dietrich

0

두 부분으로 답하겠습니다.

  1. 테스트에만 관심이 있다면 인터페이스가 필요하지 않습니다. 그 목적을 위해 모의 프레임 워크를 사용합니다 (Java : Mockito 또는 easymock). 디자인하는 코드는 테스트 목적으로 수정해서는 안된다고 생각합니다. 테스트 가능한 코드 작성은 모듈 식 코드 작성과 동일하므로 모듈 식 (테스트 가능) 코드를 작성하고 코드 공용 인터페이스 만 테스트하는 경향이 있습니다.

  2. 나는 큰 Java 프로젝트에서 일해 왔으며 읽기 전용 (게터 만) 인터페이스를 사용 하는 것이 좋은 방법 이라고 깊이 확신하고 있습니다 (불변성의 팬입니다). 구현 클래스에는 세터가있을 수 있지만 이는 외부 레이어에 노출되어서는 안되는 구현 세부 사항입니다. 다른 관점에서 나는 상속 (모듈화, 기억?)보다 구성을 선호하므로 인터페이스도 여기에서 도움이됩니다. 나는 발로 내 자신을 쏘는 것보다 투기 적 일반성비용을 기꺼이 지불합니다 .


0

다형성 이상의 인터페이스로 프로그래밍을 시작한 이후 많은 장점을 보았습니다.

  • 클래스의 인터페이스 (공개 메소드)와 다른 클래스의 인터페이스와의 상호 작용에 대해 더 열심히 생각하게합니다.
  • 좀 더 응집력있는 작은 클래스를 작성하고 단일 책임 원칙을 따르는 데 도움이됩니다.
  • 코드를 테스트하는 것이 더 쉽습니다.
  • 클래스가 인스턴스 레벨이어야하므로 정적 클래스 / 글로벌 상태가 줄어 듭니다.
  • 전체 프로그램이 준비되기 전에 조각을 쉽게 통합하고 조립할 수 있습니다
  • 비즈니스 로직에서 객체 구성을 분리하는 의존성 주입

많은 사람들이 더 작은 수업이 더 적은 수업보다 낫다는 것에 동의 할 것입니다. 한 번에 많은 것에 집중할 필요가 없으며 각 수업은 잘 정의 된 목적을 가지고 있습니다. 다른 사람들은 더 많은 수업을 통해 복잡성을 더하고 있다고 말할 수 있습니다.

생산성을 높이기 위해 도구를 사용하는 것이 좋지만, 코드에 직접 테스트 가능성 및 모듈성을 구축하는 대신 Mock 프레임 워크에만 의존하면 장기적으로 품질이 낮은 코드가 될 것이라고 생각합니다.

결국, 그것이 더 높은 품질의 코드를 작성하는 데 도움이되었고 그 이점이 어떤 결과보다 훨씬 중요하다고 생각합니다.

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