구현 세부 사항에 연결하지 않고 단위 테스트 동작


16

이안 쿠퍼는 TDD에서 단위 테스트에 대한 켄트 벡의 원래 의도를 강의 (특별히 클래스의 방법이 아닌 행동을 테스트하기 위해)로 강요 한 TDD의 대화 에서 테스트를 구현에 결합하지 않도록 주장한다.

save X to some data source일반적인 서비스 및 리포지토리가있는 시스템과 같은 동작의 경우 테스트를 구현 세부 정보에 연결하지 않고 리포지토리를 통해 서비스 수준에서 일부 데이터 저장을 어떻게 테스트 할 수 있습니까? )? 이런 종류의 커플 링을 피하는 것은 실제로 어떤면에서 노력 / 나쁜 가치가 있습니까?


1
데이터가 리포지토리에 저장되어 있는지 테스트하려면 테스트가 실제로 리포지토리를 확인하여 데이터가 있는지 확인해야합니다. 아니면 뭔가 빠졌습니까?

내 질문은 저장소에서 특정 메소드를 호출하는 것과 같은 구현 세부 사항에 테스트를 결합하지 않거나 실제로 수행 해야하는 경우를 피하는 것에 관한 것이 었습니다.
— Andy Hunt

답변:


8

특정 예제는 일반적으로 특정 메소드가 호출되었는지 확인하여 테스트해야하는 경우 saving X to data source입니다. 외부 종속성 과 통신하는 것을 의미 하기 때문에 테스트해야하는 동작 은 통신이 예상대로 발생하는 것 입니다.

그러나 이것은 나쁜 것이 아닙니다. 응용 프로그램과 외부 종속성 간의 경계 인터페이스는 구현 세부 정보 가 아니며 실제로 시스템 아키텍처에 정의되어 있습니다. 이는 그러한 경계가 변경되지 않을 것임을 의미합니다 (또는 필요한 경우 가장 적은 종류의 변경 일 것입니다). 따라서 테스트를 repository인터페이스에 결합 하면 너무 많은 문제가 발생하지 않아야합니다 (그렇다면 인터페이스가 애플리케이션의 책임을 훔치지 않는지 고려하십시오).

이제 UI, 데이터베이스 및 기타 외부 서비스와 분리 된 응용 프로그램의 비즈니스 규칙 만 고려하십시오. 이것은 코드의 구조와 동작을 자유롭게 변경할 수 있어야한다는 것입니다. 애플리케이션의 전체 동작에 변화가없는 경우에도 커플 링 테스트와 구현 세부 사항으로 인해 프로덕션 코드보다 더 많은 테스트 코드를 변경해야합니다. 여기에서 테스트 속도를 높이는 State대신 테스트 를 Interaction진행할 수 있습니다.

추신 : 주 또는 상호 작용에 의한 테스트가 TDD의 유일한 진정한 방법인지 말할 의도는 없습니다. 올바른 작업에 올바른 도구를 사용하는 것이 문제라고 생각합니다.


"외부 종속성과 통신"을 언급 할 때 외부 종속성을 테스트중인 장치 외부 또는 시스템 외부의 외부 종속성으로 언급하고 있습니까?
— Andy Hunt

"외부 의존성"이란 애플리케이션의 플러그인으로 간주 할 수있는 모든 것을 의미합니다. 응용 프로그램이란 비즈니스 규칙을 의미하며 지속성 또는 UI에 사용할 프레임 워크와 같은 모든 종류의 세부 사항을 무시합니다. 밥 삼촌이 다음과 같이 더 잘 설명 할 수 있다고 생각합니다. youtube.com/watch?v=WpkDN78P884
— MichelHenrich

나는 이것이 "기능"또는 "행동"에 기초하여 테스트하고 특징 또는 행동마다 하나의 테스트 (또는 하나의 변수, 즉 다양한 파라미터)를 테스트하기위한 이상적인 접근법이라고 생각한다. 그러나 TDD를 수행하기 위해 기능에 대해 "행복한"테스트를 1 회 수행하면 해당 기능에 대해 하나의 거대한 커밋 (및 코드 검토)을 수행하게되므로 이는 잘못된 생각입니다. 이것을 어떻게 피할 수 있습니까? 해당 기능의 일부와 테스트와 관련된 모든 코드를 작성한 다음 후속 커밋에 기능의 나머지를 점진적으로 추가 하시겠습니까?
— Jordan

구현과 결합되는 실제 테스트 예제를보고 싶습니다.
— PositiveGuy

7

그 대화에 대한 나의 해석은 다음과 같습니다.

  • 클래스가 아닌 테스트 구성 요소
  • 인터페이스 포트를 통해 구성 요소를 테스트하십시오.

대화에서 언급되지는 않았지만 조언에 대한 가정 된 내용은 다음과 같습니다.

  • 유틸리티 라이브러리 나 프레임 워크가 아닌 사용자를위한 시스템을 개발하고 있습니다.
  • 테스트의 목표 는 경쟁력있는 예산 내에서 가능한 한 많은 것을 성공적으로 제공하는 것입니다.
  • 구성 요소는 C # / Java와 같이 성숙하고 정적 인 유형의 단일 언어로 작성됩니다.
  • 컴포넌트는 10000-50000 라인 정도이다. Maven 또는 VS 프로젝트, OSGI 플러그인 등
  • 구성 요소는 단일 개발자 또는 밀접하게 통합 된 팀이 작성합니다.
  • 육각형 아키텍처 와 같은 용어와 접근 방식을 따르고 있습니다.
  • 컴포넌트 포트는 http / SQL / XML / bytes / ...로 전환하여 로컬 언어 및 해당 유형 시스템을 떠나는 곳입니다.
  • 모든 포트를 랩핑하는 것은 Java / C #의 의미로 유형이 지정된 인터페이스로, 스위치 기술로 구현을 전환 할 수 있습니다.

따라서 구성 요소 테스트는 단위 테스트라고 할 수있는 가장 큰 범위입니다. 이것은 일부 사람들, 특히 학계에서이 용어를 사용하는 방법과는 다릅니다. 일반적인 단위 테스트 도구 자습서의 예제와 다릅니다. 그러나 하드웨어 테스트에서 그 기원과 일치합니다. 보드 및 모듈은 와이어 및 나사가 아닌 장치 테스트를 거쳤습니다. 또는 적어도 나사를 테스트하기 위해 모의 보잉을 만들지 마십시오 ...

그것으로부터 외삽하고 내 자신의 생각을 던져

  • 모든 인터페이스는 입력, 출력 또는 공동 작업자 (예 : 데이터베이스)가됩니다.
  • 입력 인터페이스 를 테스트 합니다. 메소드를 호출하고 리턴 값을 지정하십시오.
  • 출력 인터페이스 를 조롱 합니다. 주어진 테스트 케이스에 대해 예상되는 메소드가 호출되었는지 확인하십시오.
  • 공동 작업자 를 위조 합니다. 간단하지만 작동하는 구현을 제공하십시오

적절하고 깨끗하게하면 조롱 도구는 거의 필요하지 않습니다. 시스템 당 몇 번만 사용됩니다.

데이터베이스는 일반적으로 공동 작업자이므로 조롱하기보다는 위조됩니다. 손으로 구현하는 것은 고통 스러울 것입니다. 운 좋게도 그런 것들이 이미 존재합니다 .

기본 테스트 패턴은 일련의 작업을 수행하는 것입니다 (예 : 문서 저장 및 다시로드). 작동하는지 확인하십시오. 이것은 다른 테스트 시나리오와 동일합니다. (작동) 구현 변경으로 인해 그러한 테스트가 실패 할 가능성이 없습니다.

테스트중인 시스템에서 데이터베이스 레코드를 쓰지만 읽지 않는 경우는 예외입니다. 예 : 감사 로그 또는 유사 이것들은 출력이므로 조롱해야합니다. 테스트 패턴은 일련의 작업을 수행하는 것입니다. 지정된대로 메소드와 인수로 감사 인터페이스가 호출되었는지 확인하십시오.

여기에서도 mockito 와 같은 유형 안전 모의 도구를 사용하는 경우 인터페이스 메소드의 이름을 바꾸면 테스트 실패가 발생할 수 없습니다. 테스트가로드 된 IDE를 사용하는 경우 메소드 이름 바꾸기와 함께 리팩터링됩니다. 그렇지 않으면 테스트가 컴파일되지 않습니다.


인터페이스 포트의 구체적인 예를 설명해 줄 수 있습니까?
— PositiveGuy

출력 인터페이스의 예는 무엇입니까? 코드를 구체적으로 지정할 수 있습니까? 입력 인터페이스와 동일합니다.
— PositiveGuy

인터페이스 (Java / C # 의미)는 외부 세계 (d / b, socket, http, ....)와 통신 할 수있는 포트를 래핑합니다. 출력 인터페이스는 포트를 통해 외부에서 오는 반환 값을 가진 메서드가없는 인터페이스이며 예외 또는 이에 상응하는 것입니다.
— soru

입력 인터페이스는 정반대이며 공동 작업자는 입력과 출력입니다.
— soru

1
비디오에서 설명한 것과 완전히 다른 디자인 방식과 용어 집합에 대해 이야기하고 있다고 생각합니다. 그러나 리포지토리 (예 : 데이터베이스)의 90 %는 입력 또는 출력이 아닌 공동 작업자입니다. 따라서 인터페이스는 협업 인터페이스입니다.
— soru

0

내 제안은 상태 기반 테스트 방법을 사용하는 것입니다.

GIVEN 우리는 알려진 상태의 테스트 DB를 가지고 있습니다

언제 이 서비스는 인수 X로 호출

THEN 읽기 전용 리포지토리 메서드를 호출하고 반환 된 값을 확인하여 DB가 원래 상태에서 예상 상태로 변경되었다고 가정합니다.

그렇게하면 서비스의 내부 알고리즘에 의존하지 않으며 테스트를 변경하지 않고도 구현을 리팩터링 할 수 있습니다.

여기서 유일한 연결은 서비스 메소드 호출과 DB에서 데이터를 읽는 데 필요한 저장소 호출에 대한 것입니다.

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