우리는 3,000 건에 가까운 테스트를 작성했습니다. 데이터는 하드 코딩되었으며 코드 재사용은 거의 없습니다. 이 방법론은 엉덩이에 우리를 물기 시작했다. 시스템이 변경됨에 따라 깨진 테스트를 수정하는 데 더 많은 시간을 소비하게됩니다. 단위, 통합 및 기능 테스트가 있습니다.
내가 찾고있는 것은 관리 가능하고 유지 관리 가능한 테스트를 작성하는 결정적인 방법입니다.
프레임 워크
우리는 3,000 건에 가까운 테스트를 작성했습니다. 데이터는 하드 코딩되었으며 코드 재사용은 거의 없습니다. 이 방법론은 엉덩이에 우리를 물기 시작했다. 시스템이 변경됨에 따라 깨진 테스트를 수정하는 데 더 많은 시간을 소비하게됩니다. 단위, 통합 및 기능 테스트가 있습니다.
내가 찾고있는 것은 관리 가능하고 유지 관리 가능한 테스트를 작성하는 결정적인 방법입니다.
프레임 워크
답변:
그것들이 "파손 된 단위 테스트"라고 생각하지 마십시오.
그것들은 당신의 프로그램이 더 이상 지원하지 않는 사양입니다.
이를 "테스트 수정"이라고 생각하지 말고 "새로운 요구 사항 정의"로 생각하십시오.
테스트는 다른 방법이 아닌 응용 프로그램을 먼저 지정해야합니다.
작동한다는 것을 알기 전까지는 작동중인 구현이 있다고 말할 수 없습니다. 테스트 할 때까지 작동한다고 말할 수 없습니다.
당신을 안내 할 몇 가지 참고 사항 :
Don't think of it as "fixing the tests", but as "defining new requirements".
설명하는 것은 실제로 그렇게 나쁜 것은 아니지만 테스트에서 발견 한 더 깊은 문제에 대한 포인터
시스템이 변경됨에 따라 깨진 테스트를 수정하는 데 더 많은 시간을 소비하게됩니다. 단위, 통합 및 기능 테스트가 있습니다.
코드를 변경할 수 있고 테스트가 중단 되지 않으면 의심 스러울 것입니다. 합법적 인 변경과 버그의 차이점은 요청 된 사실이며, 요청 된 것은 테스트에 의해 정의 된 것입니다 (TDD 가정).
데이터는 하드 코딩되었습니다.
테스트에서 하드 코딩 된 데이터는 좋은 것입니다. 테스트는 증거가 아닌 위조로 작동합니다. 계산이 너무 많으면 테스트가 팽팽해질 수 있습니다. 예를 들면 다음과 같습니다.
assert sum([1,2,3]) == 6
assert sum([1,2,3]) == 1 + 2 + 3
assert sum([1,2,3]) == reduce(operator.add, [1,2,3])
추상화가 높을수록 알고리즘에 더 가까워지고 그에 따라 acutal 구현과 자체 비교에 더 가까워집니다.
코드 재사용이 거의 없음
테스트에서 코드를 가장 잘 재사용하는 것은 jUnits 에서처럼 assertThat
테스트를 단순하게 유지하기 때문에 imho 'Checks' 입니다. 게다가, 테스트가 코드를 공유하기 위해 리팩토링 될 수 있다면, 테스트 된 실제 코드도 그럴 수 있으므로 리팩토링 된베이스를 테스트하는 테스트로 테스트가 줄어 듭니다.
나는이 문제도 가지고있다. 내 개선 된 다음과 같은 접근 방법은있다 :
단위 테스트 가 무언가를 테스트하는 유일한 방법이 아니라면 작성하지 마십시오 .
단위 테스트의 진단 비용과 수정 시간이 가장 낮습니다. 이를 통해 귀중한 도구가됩니다. 문제는 명백한 마일리지가 다를 수 있기 때문에 단위 테스트는 코드 질량을 유지하는 비용을 감당하기에는 너무 소박하다는 것입니다. 나는 맨 아래에 예를 썼다.
해당 구성 요소에 대한 단위 테스트와 동등한 경우 어설 션을 사용하십시오 . 어설 션에는 디버그 빌드 전체에서 항상 확인 되는 멋진 속성이 있습니다. 따라서 별도의 테스트 단위로 "직원"클래스 제약 조건을 테스트하는 대신 시스템의 모든 테스트 사례를 통해 직원 클래스를 효과적으로 테스트합니다 . 어설 션은 또한 단위 테스트만큼 코드 질량을 늘리지 않는다는 좋은 특성을 가지고 있습니다.
누군가 나를 죽이기 전에 : 어설 션에서 프로덕션 빌드가 충돌해서는 안됩니다. 대신 "오류"수준으로 기록해야합니다.
아직 생각하지 않은 사람에게주의를 기울이려면 사용자 나 네트워크 입력에 대해 아무 것도 주장하지 마십시오. 큰 실수입니다.
최신 코드 기반에서 어설 션이 분명한 곳이면 어디에서나 단위 테스트를 신중하게 제거했습니다. 이로 인해 전체 유지 보수 비용이 크게 절감되었으며 더 행복한 사람이되었습니다.
시스템 / 통합 테스트를 선호 하여 모든 기본 흐름과 사용자 경험에 맞게 구현하십시오. 코너 케이스가 여기에있을 필요는 없습니다. 시스템 테스트는 모든 구성 요소를 실행하여 사용자 쪽의 동작을 확인합니다. 따라서 시스템 테스트는 반드시 느리게 진행되므로 중요한 테스트를 작성하십시오 (더 이상, 더 이상은 아님). 가장 중요한 문제를 발견하게됩니다. 시스템 테스트는 유지 관리 오버 헤드가 매우 낮습니다.
어설 션을 사용하고 있기 때문에 각 시스템 테스트는 동시에 수백 개의 "단위 테스트"를 실행한다는 것을 기억하는 것이 중요합니다. 가장 중요한 것은 여러 번 실행된다는 것이 확실합니다.
기능적으로 테스트 할 수있는 강력한 API를 작성하십시오. API로 인해 작동하는 구성 요소를 자체적으로 검증하기가 너무 어려운 경우 기능 테스트는 어색하며 의미가 없습니다. 좋은 API 디자인 a) 테스트 단계를 간단하게하고 b) 명확하고 귀중한 주장을 얻습니다.
기능 테스트는 프로세스 장벽을 가로 질러 일대 다 또는 다 대다 (대다로 다 대다)로 통신하는 구성 요소가있는 경우에 올바르게 달성하기가 가장 어렵습니다. 단일 구성 요소에 더 많은 입력 및 출력이 연결 될수록 기능 테스트는 더 어렵습니다. 실제로 기능을 테스트하려면 이들 중 하나를 분리해야하기 때문입니다.
"단위 테스트를 작성하지 마십시오"문제에 대한 예를 들어 보겠습니다.
TEST(exception_thrown_on_null)
{
InternalDataStructureType sink;
ASSERT_THROWS(sink.consumeFrom(NULL), std::logic_error);
try {
sink.consumeFrom(NULL);
} catch (const std::logic_error& e) {
ASSERT(e.what() == "You must not pass NULL as a parameter!");
}
}
이 테스트의 저자는 기여하지 않는 일곱 개 라인을 추가했습니다 모두에서 최종 제품의 검증에 있습니다. 사용자는해야 결코 A) 아무도 NULL을 전달 없어야하기 때문에 (그래서이 다음 주장을 쓰기) 또는 b)는 NULL의 경우는 약간 다른 동작이 발생할해야 하나, 이런 일을 볼 수 없습니다. 사례가 (b) 인 경우 실제로 해당 행동을 검증하는 테스트를 작성하십시오.
저의 철학은 구현 아티팩트를 테스트해서는 안된다는 것입니다. 실제 출력으로 간주 될 수있는 것만 테스트해야합니다. 그렇지 않으면 단위 테스트 (특정 구현을 강제하는)와 구현 자체 사이에 기본 대량의 코드를 두 번 쓰는 것을 피할 수있는 방법이 없습니다.
여기서 단위 테스트를위한 좋은 후보자가 있다는 점에 유의해야합니다. 실제로, 단위 테스트가 무언가를 검증하고 이러한 테스트를 작성하고 유지하는 것이 가치가있는 유일한 적절한 수단 인 상황도 있습니다. 내 머리 위에이 목록에는 사소한 알고리즘, API의 노출 된 데이터 컨테이너 및 "복잡한"것으로 보이는 고도로 최적화 된 코드가 포함됩니다 (일명 "다음 사람이 망칠 것").
다음과 같은 구체적인 조언을 드리겠습니다. 유닛 테스트가 중단 될 때 신중하게 삭제하고 "이것이 출력입니까, 아니면 코드를 낭비합니까?"라는 질문을합니다. 당신은 아마도 당신의 시간을 낭비하는 것들의 수를 줄이는 데 성공할 것입니다.
단위 테스트가 매력처럼 작동하는 것처럼 보입니다. 그것은 요점의 변화이기 때문에 변경하기에 너무 약한 것이 좋습니다 . 코드 중단 테스트의 작은 변경으로 프로그램 전체에서 오류가 발생할 가능성을 제거 할 수 있습니다.
그러나 분석법이 실패하거나 예기치 않은 결과를 초래할 수있는 조건을 테스트하기 만하면됩니다. 이렇게하면 사소한 것이 아니라 실제 문제가있는 경우 유닛 테스트가 "중단"되기 쉽습니다.
비록 당신이 프로그램을 크게 재 설계하고있는 것 같습니다. 그러한 경우 필요한 모든 작업을 수행하고 이전 테스트를 제거한 후 새 테스트로 교체하십시오. 단위 테스트 복구는 프로그램의 급격한 변화로 인해 수정하지 않은 경우에만 유용합니다. 그렇지 않으면 새로 작성된 프로그램 코드 섹션에 적용하기 위해 테스트를 다시 작성하는 데 너무 많은 시간을 투자하고 있음을 알 수 있습니다.
다른 사람들이 더 많은 정보를 얻을 것이라고 확신하지만 내 경험상 다음과 같은 중요한 것들이 있습니다.
Gerard Meszaros의 XUnit 테스트 패턴을 확실히 살펴보아야 합니다 . 테스트 코드를 재사용하고 중복을 피하기 위해 많은 레시피 가 포함 된 훌륭한 섹션 이 있습니다.
테스트가 약하면 더블을 테스트하기에 충분히 의지하지 않을 수도 있습니다. 특히, 각 단위 테스트가 시작될 때 객체의 전체 그래프를 재생성하면 테스트의 정렬 섹션이 너무 커져서 정렬 섹션을 상당한 수의 테스트에서 다시 작성해야하는 상황에 처할 수 있습니다. 가장 일반적으로 사용되는 클래스 중 하나가 변경되었습니다. Mocks와 Stub은 관련 테스트 컨텍스트를 갖기 위해 재수 화해야하는 객체의 수를 줄임으로써 여기에서 도움을 줄 수 있습니다.
모의 및 스텁을 통해 테스트 설정에서 중요하지 않은 세부 정보를 가져오고 코드를 재사용하기 위해 테스트 패턴을 적용하면 취약성이 크게 감소합니다.