TDD : 단단히 결합 된 객체 모의


10

때때로 객체는 단단히 결합되어야합니다. 예를 들어 CsvFile클래스는 CsvRecord클래스 (또는 ICsvRecord인터페이스) 와 밀접하게 작동해야합니다 .

그러나 과거에 배운 내용에서 테스트 중심 개발의 주요 원칙 중 하나는 "한 번에 여러 클래스를 테스트하지 마십시오"입니다. 의미하는 ICsvRecord실제 인스턴스 대신 모의 또는 스텁 을 사용해야합니다 CsvRecord.

그러나이 접근법을 시도한 후에 나는 CsvRecord수업 을 조롱 하면 약간 털이 나올 수 있음을 알았습니다 . 그러면 두 가지 결론 중 하나가 나옵니다.

  1. 단위 테스트를 작성하는 것은 어렵습니다! 코드 냄새입니다! 리팩터링!
  2. 모든 단일 의존성을 모의하는 것은 불합리합니다.

모의를 실제 CsvRecord인스턴스로 바꾸면 상황이 훨씬 순조로워졌습니다. 다른 사람들의 생각을 둘러 볼 때이 블로그 게시물을 우연히 발견했습니다 . 위의 # 2를 지원하는 것 같습니다. 자연스럽게 단단히 연결된 객체의 경우 조롱에 대해 너무 걱정하지 않아도됩니다.

내가 길을 벗어 났습니까? 위의 가정 # 2에 대한 단점이 있습니까? 실제로 디자인 리팩토링에 대해 생각해야합니까?


1
"단위 테스트"에서 "단위"는 반드시 하나의 클래스 여야한다는 것이 일반적인 오해라고 생각합니다. 귀하의 예는 두 클래스가 하나의 단위를 형성하는 것이 더 나은 경우를 보여줍니다. 그러나 내가 틀리지 말아라. 나는 Robert Harvey의 대답에 전적으로 동의한다.
Doc Brown

답변:


11

두 클래스 사이에 조정이 필요한 경우 두 CsvCoordinator클래스를 캡슐화 하는 클래스를 작성 하고 테스트하십시오.

그러나 나는 CsvRecord독립적으로 테스트 할 수없는 개념에 이의를 제기합니다 . CsvRecord기본적으로 DTO 클래스입니까? 아마도 몇 가지 도우미 메서드가있는 필드 모음 일뿐입니다. 그리고 CsvRecord다른 상황에서도 사용될 수 있습니다 CsvFile. CsvRecord예를 들어, 모음 또는 배열을 가질 수 있습니다 .

CsvRecord먼저 테스트하십시오 . 모든 테스트를 통과했는지 확인하십시오. 그런 다음 시험 중에 수업에 사용 CsvRecord하십시오 CsvFile. 사전 테스트 된 스텁 / 모의로 사용하십시오. 관련 테스트 데이터로 채우고에 전달한 다음 이에 CsvFile대한 테스트 사례를 작성하십시오.


1
예, CsvRecord는 가장 독립적으로 테스트 가능합니다. 문제는 CsvRecord에서 문제가 발생하면 CsvData 테스트가 실패한다는 것입니다. 그러나 나는 그것이 큰 문제라고 생각하지 않습니다.
Phil

1
나는 당신이 그런 일이 일어나길 원한다고 생각합니다. :)
Robert Harvey

1
@RobertHarvey : 이론적으로 CsvRecord와 CsvFile이 상당히 복잡한 클래스가되면 문제가 될 수 있으며, CsvFile에 대한 테스트가 중단되면 CsvFile 또는 CsvRecord의 문제인지 즉시 알 수 없습니다. 그러나 나는 이것이 가상의 경우라고 생각합니다. 실제 프로그램을 위해 그러한 클래스를 프로그래밍하는 작업이 있다면, 당신이 그것을 묘사하는 방식으로 정확하게 할 것입니다.
Doc Brown

2
@Phil : CsvRecord중단되면 분명히 CsvData실패합니다. 그러나 CsvRecord먼저 테스트하면되기 때문에 괜찮습니다 CsvFile. 테스트에 실패하면 테스트는 의미가 없습니다. 오류 CsvRecord와 오류를 구분할 수 있습니다 CsvFile.
tdammers

5

한 번에 한 클래스를 테스트하는 이유는 한 클래스에 대한 테스트가 두 번째 클래스의 동작에 종속되지 않기를 원하기 때문입니다. 즉, 클래스 A 테스트에서 클래스 B의 기능을 수행하는 경우 클래스 B를 모의하여 클래스 B 내의 특정 기능에 대한 종속성을 제거해야합니다.

같은 클래스는 CsvRecord주로 데이터를 저장하기위한 것 같습니다-자체 기능이 너무 많은 클래스는 아닙니다. 즉, 생성자, getter, setter가있을 수 있지만 실질적인 논리를 가진 메소드는 없습니다. 물론, 나는 여기서 추측하고 있습니다. 아마도 당신은 CsvRecord수많은 복잡한 계산을 수행 하는 클래스를 작성했을 것입니다 .

그러나 CsvRecord그 자체의 실제 논리가 없다면 그것을 조롱하여 얻을 수있는 것은 없습니다. 이것은 실제로 오래된 최대 값입니다 - "값 객체를 조롱하지 마십시오" .

따라서 (다른 클래스의 테스트를 위해) 특정 클래스를 조롱할지 여부를 고려할 때 클래스가 가지고있는 자체 로직의 양과 테스트 과정에서 실행될 로직의 양을 고려해야합니다.


+1. 결과가 둘 이상의 객체 동작의 정확성에 의존하는 모든 테스트는 단위 테스트가 아니라 통합 테스트입니다. 실제 단위 테스트를 받으려면 이러한 객체 중 하나를 조롱해야합니다. 실제 동작이없는 객체에는 적용되지 않으며 예를 들어 getter와 setter 만 있습니다.
guillaume31

1

2 번은 괜찮습니다. 개념이 밀접하게 결합되면 사물은 밀접하게 결합 될 수 있으며 밀접하게 결합 되어야합니다 . 이것은 드물고 일반적으로 피해야하지만 예에서 제공 한 예가 적합합니다.


0

"커플 링 된"클래스는 서로 상호 의존적입니다. CsvRecord는이를 포함하는 CsvFile을 실제로 신경 쓰지 않아야하므로 종속성은 한 가지 방법으로 만 진행됩니다. 괜찮아요 . 타이트한 커플 링 이 아닙니다 .

결국 클래스에 변수 문자열 이름이 포함되어 있으면 클래스가 문자열에 단단히 연결되어 있다고 주장하지 않습니까?

따라서 CsvRecord에서 원하는 동작을 단위 테스트합니다.

그런 다음 조롱 프레임 워크 (Mockito는 훌륭합니다)를 사용하여 장치가 올바르게 의존하는 객체와 상호 작용하는지 테스트하십시오. 실제로 테스트하려는 동작은 CsvFile이 CsvRcord를 예상 된 방식으로 처리하는 것입니다. CvsRecord의 내부 작업은 중요하지 않습니다. CvsFile이 CvsFile과 통신하는 방식입니다.

마지막으로, TDD는하지 않습니다 단위 테스트에 대한. 더 큰 구성 요소의 작동 방식, 즉 사용자 스토리 또는 시나리오의 기능적 동작을 살펴 보는 기능 테스트를 시작할 수 있습니다. 단위 테스트는 기대치를 설정하고 조각을 확인하며 기능 테스트는 전체적으로 동일하게 수행됩니다.


1
-1, 긴밀한 커플 링이 반드시 주기적 종속성을 의미하는 것은 아니며 오해입니다. 이 예에서는 (단, 라운드는 아닙니다 CsvFile ) 단단히 결합되어 CsvRecord있습니다. OP는 테스트 CsvFileCsvRecord통해 좋은 아이디어인지 아닌지를 묻습니다 ICsvRecord.
Doc Brown

2
@DocBrown : 커플 링이 꽉 찼는 지 여부는 CsvFile내부 작업 CsvRecord, 즉 파일이 레코드에 대해 가정 한 양에 따라 달라집니다 . 인터페이스는 이러한 가정 (또는 다른 가정이 없음)을 문서화하고 적용하는 데 도움이되지만 인터페이스를 사용하면 다른 레코드 클래스를에 연결할 수 있다는 점을 제외하면 커플 링 양은 동일하게 유지됩니다 CsvFile. 인터페이스를 소개하면 커플 링이 줄었다 고 말하는 것이 어리석은 일입니다.
tdammers

0

여기에는 두 가지 질문이 있습니다. 첫 번째는 객체를 조롱하는 것이 바람직하지 않은 상황이있는 경우입니다. 다른 훌륭한 답변에서 볼 수 있듯이 의심 할 여지없이 사실입니다. 두 번째 질문은 특정 사례가 그러한 상황 중 하나인지 여부입니다. 그 질문에 나는 확신하지 못한다.

아마도 클래스를 조롱하지 않는 가장 일반적인 이유는 클래스가 가치 클래스 인 것입니다. 그러나 규칙의 원인을 살펴 봐야합니다. 그것은 조롱 된 클래스가 어떻게 든 나쁘지 않기 때문이 아니라, 본질적으로 원래와 동일하기 때문입니다. 이 경우 원래 클래스를 사용하면 단위 테스트가 더 쉬워지지 않습니다.

코드가 리팩토링이 도움이되지 않는 드문 예외 중 하나 일 수 있지만 부지런한 리팩토링 노력이 효과가없는 경우에만 선언해야합니다. 노련한 개발자조차도 자신의 디자인에 대한 대안을 찾는 데 어려움을 겪을 수 있습니다. 개선 할 수있는 방법을 생각할 수 없다면 경험이있는 사람에게 다시 한 번 살펴 보라고 요청하십시오.

대부분의 사람들은 당신 CsvRecord이 가치 클래스 라고 가정하는 것 같습니다 . 하나 만들어보십시오. 가능하다면 불변으로 만드십시오. 서로 포인터가있는 두 개의 오브젝트가있는 경우 그 중 하나를 제거하고 작동 방법을 찾으십시오. 클래스와 함수를 분리 할 장소를 찾으십시오. 클래스를 분할하기에 가장 좋은 장소는 항상 파일의 실제 레이아웃과 일치하지 않을 수 있습니다. 클래스의 부모 / 자식 관계를 바꾸십시오. csv 파일을 읽고 쓰는 별도의 클래스가 필요할 수 있습니다. 파일 I / O 및 상위 계층에 대한 인터페이스를 처리하기 위해 별도의 클래스가 필요할 수 있습니다. 리팩터링 할 수 없다고 선언하기 전에 시도해야 할 것이 많이 있습니다.

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