답변:
나는 이것이 내가 생각할 수있는 오해라고 생각합니다.
프로덕션 코드를 테스트하는 테스트 코드는 전혀 비슷하지 않습니다. 나는 파이썬에서 시연 할 것이다 :
def multiply(a, b):
"""Multiply ``a`` by ``b``"""
return a*b
그런 다음 간단한 테스트는 다음과 같습니다.
def test_multiply():
assert multiply(4, 5) == 20
두 함수 모두 비슷한 정의를 가지고 있지만 두 가지 기능이 매우 다릅니다. 중복 코드가 없습니다. ;-)
또한 사람들은 본질적으로 테스트 기능 당 하나의 주장을 갖는 중복 테스트를 작성합니다. 이것은 광기이며 사람들이 이것을하는 것을 보았습니다. 이것은 이다 나쁜 관행.
def test_multiply_1_and_3():
"""Assert that a multiplication of 1 and 3 is 3."""
assert multiply(1, 3) == 3
def test_multiply_1_and_7():
"""Assert that a multiplication of 1 and 7 is 7."""
assert multiply(1, 7) == 7
def test_multiply_3_and_4():
"""Assert that a multiplication of 3 and 4 is 12."""
assert multiply(3, 4) == 12
1000 개 이상의 유효 코드 줄에 대해이 작업을 수행한다고 상상해보십시오. 대신 '기능'별로 테스트합니다.
def test_multiply_positive():
"""Assert that positive numbers can be multiplied."""
assert multiply(1, 3) == 3
assert multiply(1, 7) == 7
assert multiply(3, 4) == 12
def test_multiply_negative():
"""Assert that negative numbers can be multiplied."""
assert multiply(1, -3) == -3
assert multiply(-1, -7) == 7
assert multiply(-3, 4) == -12
이제 기능이 추가 / 제거 될 때 하나의 테스트 기능 추가 / 제거 만 고려하면됩니다.
for
루프를 적용하지 않은 것을 알 수 있습니다 . 몇 가지 반복하는 것이 좋기 때문입니다. 루프를 적용했을 때 코드가 훨씬 짧습니다. 그러나 어설 션이 실패하면 모호한 메시지를 표시하는 출력이 난독 화 될 수 있습니다. 이런 일이 발생하면 테스트가 유용하지 않으며 문제 가 발생한 곳을 검사하기 위해 디버거 가 필요합니다.
assert multiply(1,3)
실패하지만에 대한 실패한 테스트 보고서도받지 않습니다 assert multiply(3,4)
.
def test_shuffle
.
assert multiply(*, *) == *
따라서 assert_multiply
함수를 정의 할 수 있습니다. 현재 시나리오에서는 행 수와 가독성에 따라 중요하지 않지만 더 긴 테스트를 통해 복잡한 어설 션, 픽스처, 픽스처 생성 코드 등을 재사용 할 수 있습니다. 이것이 최선의 방법인지는 모르겠지만 보통은 이.
DRY의 궁극적 목표가 모든 테스트 코드의 제거를 포함하지 않습니까?
아니요, DRY의 궁극적 인 목표는 실제로 모든 생산 코드를 제거한다는 의미 입니다.
테스트가 시스템이 원하는 것을 완벽하게 규정 할 수 있다면, 해당 생산 코드 (또는 이진)를 자동으로 생성하여 생산 코드 기반 자체를 효과적으로 제거하면됩니다.
이것은 실제로 모델 중심 아키텍처와 같은 접근 방식으로, 모든 것이 계산에 의해 도출되는 인간이 설계 한 단일 소스입니다.
나는 반대로 (모든 테스트를 없애는) 것이 바람직하다고 생각하지 않습니다.
단위 테스트는 의도하지 않은 변경을 더 어렵게 만드는 것이므로 의도적 인 변경을 더 어렵게 만들 수도 있습니다. 이 사실은 실제로 DRY 원칙과 관련이 있습니다.
예를 들어, MyFunction
프로덕션 코드에서 한 곳에만 호출 되는 함수 가 있고이를 위해 20 개의 단위 테스트를 작성하면 해당 함수가 호출되는 코드에 21 개의 위치가있게됩니다. 이제, MyFunction
일부 요구 사항이 변경되기 때문에 의 서명 또는 시맨틱 또는 둘 다 의 서명을 변경해야하는 경우 하나의 위치 대신 21 개의 위치를 변경할 수 있습니다. 그리고 그 이유는 실제로 DRY 원칙을 위반하는 것입니다 MyFunction
. 21 번 이상 동일한 함수 호출을 반복했습니다.
이러한 경우에 대한 올바른 접근 방식은 테스트 코드에도 DRY 원칙을 적용하는 것입니다. 20 단위 테스트를 작성할 때 단위 테스트에서 호출을 MyFunction
몇 가지 도우미 함수 (이상적으로는 하나)로 캡슐화하십시오 . 20 개의 단위 테스트. 이상적으로는 코드 호출 MyFunction
에서 프로덕션 코드와 단위 테스트 중 하나만 두 곳으로 끝납니다 . 따라서 MyFunction
나중에 서명 을 변경해야 할 경우 테스트에서 변경해야 할 곳이 거의 없습니다.
"몇 군데"는 여전히 "한 곳"( 단위 테스트 없이는 얻을 수 있는 것) 이상이지만 단위 테스트를 사용하면 얻을 수있는 코드가 적을수록 이점이 훨씬 뛰어납니다 (그렇지 않으면 단위 테스트를 완전히 수행해야 함) 잘못된).
소프트웨어를 구축하는 데 가장 큰 어려움 중 하나는 요구 사항을 포착하는 것입니다. "이 소프트웨어는 어떻게해야합니까?"라는 질문에 대답하는 것입니다. 소프트웨어는 시스템이해야 할 일을 정확하게 정의하기 위해 정확한 요구 사항이 필요하지만 소프트웨어 시스템 및 프로젝트의 요구를 정의하는 사람들은 종종 소프트웨어 나 공식 (수학) 배경이없는 사람들을 포함합니다. 요구 사항 정의가 엄격하지 않으면 소프트웨어 개발에서 소프트웨어를 요구 사항에 맞게 검증하는 방법을 찾아야했습니다.
개발 팀은 프로젝트에 대한 구어체 설명을보다 엄격한 요구 사항으로 변환하는 것을 발견했습니다. 테스트 분야는 고객이 원하는 것과 소프트웨어가 원하는 것을 이해하는 격차를 해소하기 위해 소프트웨어 개발을위한 체크 포인트로 통합되었습니다. 소프트웨어 개발자와 품질 / 테스트 팀 모두 (비공식) 사양에 대한 이해를 형성하고 각 (독립적으로) 소프트웨어 또는 테스트를 작성하여 이해가 일치하는지 확인합니다. (정확하지 않은) 요구 사항을 이해하기 위해 다른 사람을 추가하면 요구 사항의 정확성을 더욱 높이기 위해 질문과 다른 관점이 추가되었습니다.
승인 테스트는 항상 있었으므로 자동 및 단위 테스트를 작성하기 위해 테스트 역할을 확장하는 것이 당연했습니다. 문제는 프로그래머가 테스트를 수행하도록하는 것을 의미했기 때문에 품질 보증에서 테스트를 수행하는 프로그래머로 관점을 좁혔습니다.
모든 테스트에서 테스트가 실제 프로그램과 거의 다르면 테스트를 잘못하고있을 수 있습니다. Msdy 제안은 테스트의 내용에 초점을 맞추고 방법에 대해서는 덜 집중하는 것입니다.
아이러니 한 점은 구어체 설명에서 요구 사항의 공식 사양을 포착하기보다는 테스트를 자동화하기위한 코드로 포인트 테스트를 구현하기로 선택한 것입니다. 소프트웨어가 대답하기 위해 구축 할 수있는 공식적인 요구 사항을 생성하는 대신, 공식적인 논리를 사용하여 소프트웨어를 구축하는 것이 아니라 몇 가지 점을 테스트하는 방법이 사용되었습니다. 이것은 타협이지만 상당히 효과적이고 상대적으로 성공했습니다.
단위 테스트에는 이미 언급했듯이 테스트중인 코드의 중복이 포함되어서는 안됩니다.
그러나 단위 테스트는 일반적으로 "프로덕션"코드만큼 건조하지는 않습니다. 특히 테스트에서 설정이 비슷하지만 동일하지는 않기 때문에 특히 모의중인 많은 종속성이있는 경우 / 가짜
물론 이런 종류의 일을 일반적인 설정 방법 (또는 설정 방법 세트)으로 리팩터링하는 것이 가능합니다 ...하지만 이러한 설정 방법은 매개 변수 목록이 길고 취하기 쉽다는 것을 알았습니다.
실용적이 되십시오. 유지 관리 성을 손상시키지 않고 설치 코드를 통합 할 수 있다면 반드시 그렇게하십시오. 그러나 대안이 복잡하고 취하기 쉬운 설정 방법이라면 테스트 방법에서 약간의 반복이 가능합니다.
현지 TDD / BDD 전도자는 이런 식으로 다음과 같이
말합니다 . "생산 코드는 건조해야합니다. 그러나 테스트가 촉촉한 것이 좋습니다."
테스트는 기본적으로 코드와 동일한 것을 표현하는 것으로 보이므로 코드의 복제 (개념이 아닌 구현)입니다.
이것은 사실이 아니며 테스트는 유스 케이스를 설명하지만 코드는 유스 케이스를 전달하는 알고리즘을 설명하므로 더 일반적입니다. TDD를 통해 유스 케이스 작성 (아마도 사용자 스토리를 기반으로 작성)으로 시작한 후 이러한 유스 케이스를 전달하는 데 필요한 코드를 구현합니다. 따라서 작은 테스트, 작은 코드 덩어리를 작성한 다음 필요한 경우 반복을 제거하기 위해 리팩터링합니다. 그것이 작동하는 방식입니다.
테스트를 통해 반복 할 수도 있습니다. 예를 들어 픽스쳐, 픽스쳐 생성 코드, 복잡한 어설 션 등을 재사용 할 수 있습니다. 나는 보통 테스트에서 버그를 방지하기 위해 이렇게합니다. 그러나 테스트가 실제로 실패하는지 먼저 테스트하는 것을 잊어 버렸습니다. , 30 분 동안 코드에서 버그를 찾고 테스트가 잘못되었을 때 ... xD