상태 저장 시스템에 대한 단위 테스트 설계


20

배경

테스트 주도 개발 은 이미 학교와 업계에서 졸업 한 후 대중화되었습니다. 나는 그것을 배우려고 노력하고 있지만, 몇 가지 중요한 것들이 여전히 나를 피합니다. TDD 지지자들은 다음과 같은 많은 것을 말합니다 (이하 "단일 주장 원칙"또는 SAP 라고 함 ).

한동안 저는 TDD 테스트가 가능한 한 단순하고 표현력 있고 우아 할 수있는 방법에 대해 생각했습니다. 이 기사에서는 가능한 한 간단하고 분해 된 테스트를 만드는 방법에 대해 설명합니다. 각 테스트에서 단일 어설 션을 목표로합니다.

출처 : http://www.artima.com/weblogs/viewpost.jsp?thread=35578

또한 다음과 같이 말합니다 (이하 "비공개 방법 원칙"또는 PMP 라고 함 ).

일반적으로 개인 메소드를 직접 단위 테스트하지 않습니다. 그것들은 비공개이기 때문에 구현 세부 사항을 고려하십시오. 아무도 그들 중 하나를 부르지 않고 특정 방식으로 작동 할 것으로 기대하지 않습니다.

대신 공용 인터페이스를 테스트해야합니다. 개인 메소드를 호출하는 메소드가 예상대로 작동하는 경우 확장하여 개인 메소드가 올바르게 작동한다고 가정합니다.

출처 : 개인 메소드를 어떻게 단위 테스트합니까?

상태

상태 저장 데이터 처리 시스템을 테스트하려고합니다. 시스템은 해당 데이터를 받기 전의 상태에 따라 정확히 동일한 데이터에 대해 다른 작업을 수행 할 수 있습니다. 시스템에서 상태를 구축하는 간단한 테스트를 고려한 다음 주어진 방법이 테스트하려는 동작을 테스트하십시오.

  • SAP는 "상태 구축 절차"를 테스트해서는 안된다고 제안합니다. 상태는 빌드 업 코드에서 예상 한 것으로 가정 한 다음 테스트하려는 하나의 상태 변경을 테스트해야합니다.

  • PMP는이 "상태 구축"단계를 건너 뛸 수 없으며 해당 기능을 독립적으로 제어하는 ​​방법 만 테스트 할 것을 제안합니다.

실제 코드의 결과는 부풀어지고 복잡하며 길고 쓰기가 어려운 테스트였습니다. 상태 전이가 바뀌면 테스트를 변경해야합니다. 작고 효율적인 테스트에서는 문제가 없지만 시간이 오래 걸리고 이러한 장기적인 테스트와 혼동됩니다. 이것은 보통 어떻게 이루어 집니까?


2
나는 당신이 이것에 대한 우아한 해결책을 찾지 못할 것이라고 생각합니다. 일반적인 접근 방식은 시스템을 상태가 양호하게 만드는 것이 아니라 이미 구축 된 것을 테스트 할 때 도움이되지 않습니다. 상태 비 저장으로 리팩토링하는 것도 비용이 들지 않습니다.
Doval


@Doval : 전화기 (SIP UserAgent)와 같은 상태를 비 상태로 만드는 방법을 설명하십시오. 이 장치의 예상 동작은 상태 전이 다이어그램을 사용하여 RFC에 지정됩니다.
Bart van Ingen Schenau

테스트를 복사 / 붙여 넣기 / 편집하고 있거나 일반적인 설정 / 해제 / 기능을 공유하기위한 유틸리티 방법을 작성하고 있습니까? 일부 테스트 사례는 확실히 길고 부풀어 오를 수 있지만 이것이 일반적인 것은 아닙니다. 상태 저장 시스템에서는 종료 상태가 매개 변수 인 일반적인 설정 루틴이 필요하며이 루틴을 통해 테스트하려는 상태가됩니다. 또한 각 테스트가 끝나면 알려진 시작 상태 (필요한 경우)로 돌아갈 수있는 분해 방법이 있으므로 다음 테스트가 시작될 때 설정 방법이 올바르게 작동합니다.
덩크

탄젠트에서는 상태 다이어그램이 커뮤니케이션 도구이며 RFC에 있더라도 구현 지침은 아니라고 덧붙입니다. 설명 된 기능을 충족하는 한 표준을 충족합니다. RFC에 정의 된대로 복잡한 상태 전환 구현을 간단한 일반 처리 기능으로 변환 한 경우가 몇 차례있었습니다. 한 번은 "숨겨진"공통 요소의 이름을 바꾼 후 5 개 상태에 대한 몇 개의 플래그 이외의 플래그가 정확히 같은 것을 깨달았을 때 수천 줄의 코드를 제거하는 것을 기억합니다.
Dunk

답변:


15

원근법:

한 걸음 물러서서 TDD가 무엇을 도와 주려고하는지 물어 봅시다. TDD는 코드가 올바른지 판단하는 데 도움을 주려고합니다. 그리고 맞습니다. "코드가 비즈니스 요구 사항을 충족합니까?" 판매 포인트는 향후 변경이 필요하다는 것을 알고 있으며, 변경 한 후에도 코드가 올바르게 유지되도록하려는 것입니다.

세부 사항을 잃어 버리고 달성하려는 목표를 놓치기 쉽다고 생각하기 때문에 이러한 관점을 제시합니다.

원칙-SAP :

TDD의 전문가는 아니지만 단일 어설 션 원칙 (SAP)이 가르치려고하는 부분이 빠진 것 같습니다. SAP는 "한 번에 한 가지만 테스트"로 재구성 할 수 있습니다. 그러나 TOTAT는 SAP만큼 쉬운 방법은 없습니다.

한 번에 한 가지만 테스트하면 한 가지 경우에만 집중할 수 있습니다. 하나의 경로; 하나의 경계 조건; 하나의 오류 사례; 한 어떤 시험 당. 그리고 그 배후의 추진 아이디어는 테스트 사례가 실패했을 때 무슨 일이 일어 났는지 알아야하므로 문제를 더 빨리 해결할 수 있습니다. 테스트 내에서 여러 조건 (예 : 둘 이상의 것)을 테스트했는데 테스트가 실패하면 더 많은 작업이 필요합니다. 먼저 여러 사례 중 실패한 사례를 식별 한 다음 해당 사례가 실패한 이유를 파악해야합니다 .

한 번에 한 가지만 테스트하면 검색 범위가 훨씬 작아지고 결함이 더 빨리 식별됩니다. "한 번에 한 가지만 테스트"한다고해서 한 번에 둘 이상의 프로세스 출력을 볼 수있는 것은 아닙니다. 예를 들어, "알려진 올바른 경로"를 테스트 할 때 특정 결과 값 foo과 다른 값 을 볼 것으로 예상 할 수 있으며이를 테스트의 일부로 bar확인할 수 있습니다 foo != bar. 핵심은 테스트중인 사례에 따라 출력 검사를 논리적으로 그룹화하는 것입니다.

원칙-PMP :

마찬가지로, PMP (Private Method Principle)가 우리에게 무엇을 가르쳐야하는지에 대해 조금 빠져 있다고 생각합니다. PMP는 시스템을 블랙 박스처럼 취급하도록 권장합니다. 주어진 입력에 대해 주어진 출력을 가져와야합니다. 블랙 박스가 어떻게 출력을 생성하는지는 중요하지 않습니다. 출력이 입력과 정렬되도록주의하십시오.

PMP는 코드의 API 측면을 볼 때 정말 좋은 관점입니다. 또한 테스트해야 할 범위를 파악하는 데 도움이 될 수 있습니다. 인터페이스 포인트를 식별하고 계약 조건을 충족하는지 확인하십시오. 인터페이스 뒤 (일부 개인용) 방법이 어떻게 작동하는지는 신경 쓸 필요가 없습니다. 그들이해야 할 일을했는지 ​​확인하면됩니다.


적용 TDD ( 귀하 )

따라서 상황은 일반적인 응용 프로그램을 넘어 약간의 주름을 나타냅니다. 앱의 메소드는 상태 저장이므로 출력은 입력뿐만 아니라 이전에 수행 된 작업에 따라 결정됩니다. 나는 <insert some lecture>끔찍한 상태에 대해 여기에 있어야한다고 확신 하지만, 그것은 당신의 문제를 해결하는 데 도움이되지 않습니다.

다양한 잠재적 상태와 전환을 트리거하기 위해 수행해야 할 작업을 보여주는 일종의 상태 다이어그램 테이블이 있다고 가정합니다. 그렇지 않은 경우이 시스템의 비즈니스 요구 사항을 표현하는 데 도움이되므로 필요합니다.

테스트 : 먼저 상태 변경을 시행하는 일련의 테스트로 끝납니다. 이상적으로는 발생할 수있는 전체 상태 변경 범위를 테스트하는 테스트가 있지만 그 정도까지 다룰 필요가없는 몇 가지 시나리오를 볼 수 있습니다.

다음으로 데이터 처리를 검증하기위한 테스트를 구축해야합니다. 이러한 상태 테스트 중 일부는 데이터 처리 테스트를 만들 때 재사용됩니다. 예를 들어, 및 상태에 Foo()따라 다른 출력을 갖는 메소드 가 있다고 가정하십시오 . " 가 "에 있을 때 출력을 테스트하기 위해 테스트를 설정 단계로 사용하려고합니다 .InitState1ChangeFooToState1Foo()State1

내가 언급하고 싶은 접근 방식에는 몇 가지 의미가 있습니다. 스포일러, 이곳은 내가 순수 주의자들을 화나게 할 곳입니다

우선, 어떤 상황에서는 무언가를 테스트로 사용하고 다른 상황에서는 셋업으로 사용한다는 것을 받아 들여야합니다. 한편으로 이것은 SAP를 직접 위반 한 것으로 보입니다. 그러나 논리적으로 ChangeFooToState1두 가지 목적을 가지고 있다고해도 여전히 SAP가 우리에게 가르치고있는 정신을 만나고 있습니다. Foo()변경 상태를 확인해야하는 ChangeFooToState1경우 테스트로 사용합니다. 그리고 "에 Foo()있을 때 State1" " 출력의 유효성을 검사해야 할 때 ChangeFooToState1설정으로 사용 하고 있습니다.

두 번째 항목은 실제적인 관점에서 시스템에 대해 완전히 무작위 화 된 단위 테스트를 원하지 않을 것입니다. 출력 유효성 검사 테스트를 실행하기 전에 모든 상태 변경 테스트를 실행해야합니다. SAP는 이러한 주문의 기본 원칙입니다. 명백한 사항을 설명하기 위해 테스트로 실패하면 무언가를 설정으로 사용할 수 없습니다.

함께 정리 :

상태 다이어그램을 사용하여 전환을 다루는 테스트를 생성합니다. 다시 다이어그램을 사용하여 상태에 의해 구동되는 모든 입력 / 출력 데이터 처리 사례를 포괄하는 테스트를 생성합니다.

이 방법을 따르면 bloated, complicated, long, and difficult to write테스트를 좀 더 쉽게 관리 할 수 ​​있습니다. 일반적으로 결과는 더 작아야하고 더 간결해야합니다 (즉, 덜 복잡해야 함). 테스트가 더 분리되거나 모듈화되어 있음을 알아야합니다.

좋은 테스트를 작성하는 데 약간의 노력이 필요하기 때문에 프로세스가 완전히 고통받지 않을 것이라고 말하는 것은 아닙니다. 그리고 일부 경우에 두 번째 매개 변수 (상태)를 매핑하기 때문에 일부는 여전히 어려울 것입니다. 게다가, 상태 비 저장 시스템이 테스트를보다 쉽게 ​​구축 할 수있는 이유는 조금 더 분명해야합니다. 그러나이 접근 방식을 응용 프로그램에 적용하면 응용 프로그램이 올바르게 작동하고 있음을 증명할 수 있습니다.


11

일반적으로 설정 세부 사항을 기능으로 추상화하여 반복하지 않아도됩니다. 이렇게하면 기능이 변경되면 테스트에서 한 곳에서만 변경하면됩니다.

그러나 일반적으로 설정 기능조차도 부풀어 오르거나 복잡하거나 길다고 묘사하고 싶지 않습니다. 테스트에 사용하기 어려운 경우 실제 코드도 사용하기 어렵 기 때문에 인터페이스에 리팩토링이 필요하다는 신호입니다.

그것은 종종 한 클래스에 너무 많은 것을 넣는 신호입니다. 상태 저장 요구 사항이있는 경우 상태를 관리하는 클래스가 필요합니다 . 이를 지원하는 클래스는 상태 비 저장이어야합니다. SIP 예제의 경우 패킷 구문 분석은 완전히 상태 비 저장이어야합니다. 패킷을 구문 분석 한 다음 sipStateController.receiveInvite()상태 전이를 관리하는 것과 같은 것을 호출하는 클래스를 가질 수 있습니다.이 상태 전환은 자체적으로 다른 상태 비 저장 클래스를 호출하여 전화 울림과 같은 작업을 수행합니다.

이로 인해 상태 머신 클래스에 대한 단위 테스트 설정은 몇 가지 메소드 호출의 간단한 문제입니다. 상태 머신 유닛 테스트 설정에서 패킷을 작성해야하는 경우 해당 클래스에 너무 많이 넣었습니다. 마찬가지로 패킷 파서 클래스는 상태 머신 클래스 용 모의를 사용하여 설정 코드를 작성하는 것이 비교적 간단해야합니다.

즉, 상태를 완전히 피할 수는 없지만 최소화하고 격리 할 수 ​​있습니다.


기록을 위해 SIP 예제는 OP가 아닌 내 것이 었습니다. 또한 일부 상태 머신은 특정 테스트에 적합한 상태를 유지하기 위해 몇 가지 이상의 메소드 호출이 필요할 수 있습니다.
Bart van Ingen Schenau

"상태를 완전히 피할 수는 없지만 최소화하고 격리 할 수 ​​있습니다"+1 동의 할 수 없었습니다. 상태는 소프트웨어에서 필요한 악입니다.
Brandon

0

TDD의 핵심 아이디어는 먼저 테스트를 작성하여 최소한 테스트하기 쉬운 시스템으로 만드는 것입니다. 잘 작동하고, 유지 관리 가능하며, 잘 문서화되어 있기는하지만, 그렇지 않은 경우에는 여전히 테스트하기가 쉽습니다.

따라서 테스트하기 어려운 시스템을 TDD하고 끝내면 문제가 발생했습니다. 아마도 사적인 것들은 공개해야합니다. 테스트를 위해 필요한 것이기 때문입니다. 아마도 당신은 올바른 추상화 수준에서 일하고 있지 않을 것입니다. 목록처럼 단순한 것은 한 수준에서는 상태가 양호하지만 다른 수준에서는 가치가 있습니다. 또는 문맥에 해당되지 않는 조언에 너무 많은 비중을 두거나 문제가 어려울 수도 있습니다. 또는 물론 디자인이 나쁠 수도 있습니다.

원인이 무엇이든간에 간단한 테스트 코드로 더 테스트 가능하도록 시스템을 다시 작성하지 않을 것입니다. 따라서 가장 좋은 계획은 다음과 같이 약간 더 멋진 테스트 기술을 사용하는 것입니다.

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