테스트가 테스트하는 코드와 함께 테스트가 작성되지 않은 이유가 있습니까?


91

최근 에 Literate Programming 에 대해 조금 읽었으며 생각이 듭니다 . 잘 작성된 테스트, 특히 BDD 스타일 사양은 코드가 산문보다 코드의 기능을 설명하는 데 더 효과적 일 수 있습니다. 자신의 정확성을 확인합니다.

테스트 한 코드와 인라인으로 작성된 테스트는 본 적이 없습니다. 언어가 응용 프로그램과 테스트 코드를 동일한 소스 파일로 작성할 때 응용 프로그램과 테스트 코드를 간단하게 분리하지 않는 경향이 있고 (아무도 쉽게 만들지 못했음) 사람들이 테스트 코드를 응용 프로그램 코드와 분리하는 더 원칙적인 이유가 있습니까?


33
doctest를 사용하는 python과 같은 일부 프로그래밍 언어를 사용하면 그렇게 할 수 있습니다.
Simon Bergot 2013

2
코드를 설명하는 것보다 BDD 스타일 사양이 더 좋다고 생각할 수 있지만 이것이 두 가지의 조합이 더 좋지 않다는 것을 의미하지는 않습니다.
JeffO

5
여기서 논증의 절반은 인라인 문서에도 적용됩니다.
코드 InChaos

3
@Simon doctests는 심각한 테스트를하기에는 너무 단순합니다. 대부분 테스트를 위해 설계되지 않았기 때문입니다. 그것들은 자동으로 검증 될 수있는 문서에 코드 예제를 포함하기위한 것입니다. 이제 일부 사람들은 단위 테스트에도 사용하지만 최근 (지난 몇 년 동안) 취약한 혼란, 지나치게 자세한 "문서"및 기타 혼란으로 끝나는 경향이 있기 때문에 많은 단점이있었습니다.

7
계약설계 는 테스트를 간단하게하는 인라인 사양을 허용합니다.
Fuhrmanator

답변:


89

인라인 테스트에서 생각할 수있는 유일한 장점은 작성할 파일 수를 줄이는 것입니다. 최신 IDE에서는 실제로 그렇게 큰 문제는 아닙니다.

그러나 인라인 테스트에는 몇 가지 명백한 단점이 있습니다.

  • 그것은 우려의 분리를 위반 합니다. 이것은 논쟁의 여지가 있지만 기능 테스트는 구현과 다른 책임입니다.
  • 테스트 / 구현을 구별하기 위해 새로운 언어 기능을 도입해야하거나 둘 사이의 경계가 흐려질 위험이 있습니다.
  • 큰 소스 파일은 작업하기가 더 어렵습니다. 읽기가 어렵고 이해하기가 어려우면 소스 제어 충돌을 처리해야 할 가능성이 높습니다.
  • "테스터"모자를 씌우기가 더 어려워 질 것 같습니다. 구현 세부 사항을 살펴보면 특정 테스트 구현을 건너 뛸 수 있습니다.

9
그 흥미 롭군요. 내가 볼 수있는 이점은 "코더"모자를 착용했을 때 테스트에 대해 생각하고 싶지만 그 반대가 사실이 아니라는 것이 좋은 점이라고 생각합니다.
Chris Devereux

2
이러한 행을 따라 한 사람이 테스트를 만들고 다른 사람이 실제로 코드를 구현하는 것이 가능할 수 있습니다. 테스트를 인라인으로 배치하면이 작업이 더 어려워집니다.
Jim Nutt

6
내가 할 수 있다면 downvote 것입니다. 이것은 어떤 식 으로든 어떻게 대답합니까? 구현자가 테스트를 작성하지 않습니까? 구현 세부 정보를 보면 테스트를 건너 뛰는 사람들이 있습니까? "단지 너무 어려워"큰 파일에 충돌 ?? 어떻게 테스트가 구현 세부 사항과 혼동 될 수 있습니까 ???
bharal

5
@bharal 또한 "단지 너무 힘들다"고 말하면 마조히즘은 바보의 미덕이다. 실제로 해결하려는 문제를 제외하고는 모든 것이 쉬워지기를 원합니다.
deworde

3
단위 테스트는 문서로 간주 될 수 있습니다. 따라서 가독성을 높이기 위해 주석과 같은 이유로 단위 테스트를 코드에 포함시켜야합니다. 그러나 문제는 단위 테스트가 많고 예상 결과를 지정하지 않는 테스트 구현 오버 헤드가 많은 경향이 있다는 것입니다. 코드 내의 주석조차도 간결하게 유지해야하며, 더 큰 설명은 기능 외부의 주석 블록, 별도의 파일 또는 디자인 문서로 이동해야합니다. 단위 테스트는 주석처럼 테스트 된 코드를 유지할 수있을 정도로 짧은 경우가 거의 없습니다.
Steve314

36

나는 몇 가지를 생각할 수 있습니다.

  • 가독성. "실제"코드와 테스트가 산재하면 실제 코드를 읽기가 더 어려워집니다.

  • 코드 팽창. "실제"코드와 테스트 코드를 동일한 파일 / 클래스 / 더 큰 컴파일 된 파일 등을 생성 할 가능성이있는 모든 것에 혼합합니다. 이는 바인딩이 늦은 언어에 특히 중요합니다.

  • 고객 / 고객이 테스트 코드를 보지 못하게 할 수 있습니다. (I는하지 않습니다 같은 이 이유를 ...하지만 닫힌 소스 프로젝트에서 작업하는 경우, 테스트 코드는 어쨌든 고객을 도울 않을 수 있습니다.)

이제 이러한 각 문제에 대한 가능한 해결 방법이 있습니다. 그러나 IMO는 처음에 거기에 가지 않는 것이 더 간단합니다.


초기에는 자바 프로그래머들이 이런 종류의 일을 했었다는 것을 관찰 할 가치가있다. main(...)테스트를 용이하게하기 위해 클래스에 메소드를 포함시키는 것. 이 아이디어는 거의 완전히 사라졌습니다. 일종의 테스트 프레임 워크를 사용하여 테스트를 별도로 구현하는 것이 업계 관행입니다.

또한 Knuth가 생각한 Literate Programming은 소프트웨어 엔지니어링 산업에서 결코 뒤지지 않았다는 것을 관찰 할 가치가 있습니다.


4
+1 가독성 문제-특히 OO 설계에서 테스트 코드가 구현 코드보다 비례 적으로 클 수 있습니다.
Fuhrmanator

2
테스트 프레임 워크를 사용하여 지적 +1 프로덕션 코드와 동시에 좋은 테스트 프레임 워크를 사용한다고 상상할 수 없습니다.
joshin4colours

1
RE : 고객 / 고객이 테스트 코드를 보지 못하게 할 수 있습니다. (이 이유가 마음에 들지 않습니다 ...하지만 비공개 소스 프로젝트를 수행하는 경우 테스트 코드는 고객을 도울 것 같지 않습니다.) -클라이언트 시스템에서 테스트를 실행하는 것이 바람직 할 수 있습니다. 테스트를 실행하면 문제가 무엇인지 신속하게 파악하고 고객 환경의 차이점을 파악하는 데 도움이 될 수 있습니다.
sixtyfootersdude

1
@ sixtyfootersdude-그것은 꽤 특이한 상황입니다. 그리고 폐쇄 소스를 개발한다고 가정 할 경우를 대비하여 표준 바이너리 배포판에 테스트를 포함하고 싶지 않을 것입니다. (고객이 실행할 테스트가 포함 된 별도의 번들을 작성합니다.)
Stephen C

1
1) 실제 세 가지 이유를 제시 한 답의 첫 부분을 놓치셨습니까? 거기에 관련된 "비판적 사고"가있었습니다 .... 2) Java 프로그래머가이 작업을 수행했다고 말한 두 번째 부분을 놓쳤습니까? 그러나 지금은 그렇지 않습니까? 그리고 프로그래머 가이 일을 중단했다는 명백한 의미는 ... 좋은 이유 때문입니까?
Stephen C

14

실제로 Design By Contract 는이를 수행하는 것으로 생각할 수 있습니다 . 문제는 대부분의 프로그래밍 언어가 다음과 같은 코드를 작성하도록 허용하지 않는다는 것입니다.

Michael Feathers는 이에 대해 발표 했으며 코드 품질을 향상시킬 수있는 여러 가지 방법 중 하나입니다.


13

코드에서 클래스 사이의 긴밀한 연결을 피하려는 것과 같은 여러 가지 이유로 테스트와 코드 사이의 불필요한 연결을 피하는 것이 좋습니다.

창조 : 테스트와 코드는 다른 사람들에 의해 다른 시간에 작성 될 수 있습니다.

제어 : 요구 사항을 지정하기 위해 테스트를 사용하는 경우 요구 사항을 변경할 수있는 사람과 실제 코드가 아닌시기에 대해 다른 규칙이 적용되기를 바랍니다.

재사용 성 : 테스트를 인라인으로 배치하면 다른 코드와 함께 테스트를 사용할 수 없습니다.

작업을 올바르게 수행하는 코드 덩어리가 있지만 성능, 유지 관리 성 등 원하는 측면에서 많은 것을 남기고 있다고 상상해보십시오. 해당 코드를 새롭고 개선 된 코드로 교체하기로 결정했습니다. 동일한 테스트 세트를 사용하면 새 코드가 이전 코드와 동일한 결과를 생성하는지 확인할 수 있습니다.

선택성 : 테스트를 코드와 별도로 유지하면 실행할 테스트를 쉽게 선택할 수 있습니다.

예를 들어 현재 작업중인 코드와 관련된 작은 테스트 모음과 전체 프로젝트를 테스트하는 더 큰 제품군이있을 수 있습니다.


TDD는 이미 테스트 생성이 프로덕션 코드 이전 (또는 동시에) 발생한다고 말하고 동일한 코더로 수행해야한다고 말합니다. 또한 테스트는 요구 사항과 거의 유사하다는 것을 암시합니다. 물론, TDD 교리에 가입하지 않은 경우 이러한 반대 의견이 적용되지 않습니다 (허용되지만 명확하게해야합니다). 또한 "재사용 가능한"테스트 란 정확히 무엇입니까? 정의한대로 테스트하는 코드에 따라 테스트하지 않습니까?
Andres F.

1
@AndresF. 아니요, 테스트는 테스트하는 코드에만 국한되지 않습니다. 그들은 테스트하는 행동에 따라 다릅니다. 위젯이 올바르게 작동하는지 확인하는 일련의 테스트로 완성 된 위젯 모듈이 있다고 가정 해보십시오. 동료는 위젯과 동일한 작업을 수행하지만 3 배 더 빠른 BetterWidget을 제공합니다. Literate Programming이 소스 코드 내에 문서를 포함하는 것과 같은 방식으로 위젯 테스트가 위젯의 소스 코드에 임베드 된 경우 해당 테스트가 BetterWidget에 적용되어 위젯과 동일하게 작동하는지 확인할 수 없습니다.
Caleb

@AndresF. TDD를 따르지 않도록 지정할 필요가 없습니다. 우주의 기본값이 아닙니다. 재사용 포인트도. 시스템을 테스트 할 때는 내부가 아닌 입력 및 출력에 관심이 있습니다. 그런 다음 작동 방식이 다르지만 다르게 구현되는 새 시스템을 만들어야하는 경우 이전 시스템과 새 시스템 모두에서 실행할 수있는 테스트를 수행하는 것이 좋습니다. 이것은 나에게 두 번 이상 일어 났으며 때로는 오래된 시스템이 아직 생산 중이거나 나란히 실행하는 동안 새 시스템에서 작업해야 할 때가 있습니다. Facebook이 반응 테스트를 통해 '반응 섬유'를 테스트하여 패리티에 도달 한 방식을 살펴보십시오.
user1852503

10

내가 생각할 수있는 몇 가지 추가 이유는 다음과 같습니다.

  • 별도의 라이브러리에 테스트를두면 프로덕션 코드가 아닌 테스트 프레임 워크와 해당 라이브러리 만 쉽게 연결할 수 있습니다 (일부 전 처리기에서는 피할 수 있지만 더 쉬운 솔루션으로 테스트를 작성하는 경우 이러한 것을 빌드하는 이유) 별도의 장소)

  • 함수, 클래스, 라이브러리의 테스트는 일반적으로 "사용자"관점 (해당 함수 / 클래스 / 라이브러리의 사용자)에서 작성됩니다. 이러한 "코드 사용"은 일반적으로 별도의 파일 또는 라이브러리에 작성되며, 해당 상황을 모방하면 테스트가 더 명확하거나 "보다 현실적"일 수 있습니다.


5

테스트가 인라인 인 경우 제품을 고객에게 배송 할 때 테스트에 필요한 코드를 제거해야합니다. 따라서 테스트를 저장하는 추가 장소는 필요한 코드와 고객이 필요로 하는 코드 구분하기 만하면됩니다 .


9
불가능하지 않다. LP와 마찬가지로 추가 전처리 단계가 필요합니다. 예를 들어 C 또는 compile-to-js 언어로 쉽게 수행 할 수 있습니다.
Chris Devereux

나에게 지적하여 +1. 나는 그것을 나타 내기 위해 대답을 편집했다.
mhr

모든 경우에 코드 크기가 중요하다는 가정도 있습니다. 경우에 따라 중요하다고해서 모든 경우에 문제가되는 것은 아닙니다. 프로그래머가 소스 코드 크기를 최적화하도록 구동되지 않는 환경이 많이 있습니다. 이 경우에는 많은 클래스를 만들지 않을 것입니다.
zumalifeguard

5

이 아이디어는 단순히 객체 기반 또는 객체 지향 디자인의 맥락에서 "Self_Test"방법에 해당합니다. Ada와 같은 컴파일 된 객체 기반 언어를 사용하는 경우 모든 자체 테스트 코드는 프로덕션 컴파일 중에 사용되지 않은 (불러지지 않은) 컴파일러에 의해 표시되므로 모두 최적화되지 않습니다. 결과 실행 파일.

"Self_Test"방법을 사용하는 것은 매우 좋은 생각입니다. 프로그래머가 실제로 품질에 관심이 있다면 모두 그렇게 할 것입니다. 그러나 한 가지 중요한 문제는 "Self_Test"메소드는 구현 세부 사항에 액세스 할 수없고 대신 오브젝트 스펙 내에서 공개 된 다른 모든 메소드에만 의존해야한다는 점에서 강력한 규율이 ​​필요하다는 것입니다. 자체 테스트에 실패하면 구현을 변경해야합니다. 자체 테스트는 객체 메소드의 게시 된 모든 속성을 엄격하게 테스트해야하지만 특정 구현의 세부 사항에 의존해서는 안됩니다.

객체 기반 언어와 객체 지향 언어는 종종 테스트 대상 객체 외부의 메소드와 관련하여 정확하게 해당 유형의 규율을 제공합니다 (객체 사양을 강제 적용하여 구현 세부 사항에 대한 액세스를 막고 그러한 시도가 감지되면 컴파일 오류를 발생시킵니다) ). 그러나 객체 자체의 내부 메소드에는 모든 구현 세부 사항에 대한 완전한 액세스 권한이 부여됩니다. 따라서 자체 테스트 방법은 고유 한 상황에 있습니다. 자체 테스트 특성 (내부 테스트는 테스트중인 객체의 방법 임)으로 인해 내부 방법이어야하지만 외부 방법의 모든 컴파일러 규율을 받아야합니다 ( 객체의 구현 세부 사항과 독립적이어야합니다). 프로그래밍 언어가 객체를 훈련시키는 기능을 제공하는 경우는 거의 없습니다. 내부 메소드는 외부 메소드 인 것처럼. 이것은 중요한 프로그래밍 언어 디자인 문제입니다.

적절한 프로그래밍 언어 지원이없는 경우 가장 좋은 방법은 컴패니언 객체를 만드는 것입니다. 즉, 코딩하는 모든 객체 ( "Big_Object"라고 함)에 대해 이름이 "실제"객체의 이름 (이 경우 "Big_Object_Self_Test")과 연결된 표준 접미사로 구성된 두 번째 동반 객체도 생성합니다. ")이고 사양이 단일 메소드 ("Big_Object_Self_Test.Self_Test (This_Big_Object : Big_Object) return Boolean; ")로 구성되어 있습니다. 그러면 컴패니언 객체는 기본 객체의 사양에 따라 달라지며 컴파일러는 컴패니언 객체의 구현에 대해 해당 사양의 모든 분야를 완전히 시행합니다.


4

릴리스 빌드에서 테스트 코드를 제거하기가 어렵 기 때문에 인라인 테스트가 수행되지 않았 음을 나타내는 많은 의견에 대한 답변입니다. 사실이 아닙니다. 거의 모든 컴파일러와 어셈블러는 C, C ++, C #과 같은 컴파일 된 언어를 사용하여 이미이를 지원합니다. 이는 컴파일러 지시문으로 수행됩니다.

c #의 경우 (c ++도 믿고 사용하는 컴파일러에 따라 구문이 약간 다를 수 있습니다) 이것이 가능한 방법입니다.

#define DEBUG //  = true if c++ code
#define TEST /* can also be defined in the make file for c++ or project file for c# and applies to all associated .cs/.cpp files */

//somewhere in your code
#if DEBUG
// debug only code
#elif TEST
// test only code
#endif

이것은 컴파일러 지시문을 사용하므로 플래그가 설정되지 않은 경우 빌드 된 실행 파일에 코드가 존재하지 않습니다. 여러 플랫폼 / 하드웨어에 대해 "한 번 작성하고 두 번 컴파일"하는 방법도 있습니다.


2

우리는 Perl 코드와 함께 인라인 테스트를 사용합니다. 인라인 코드에서 테스트 파일을 생성하는 Test :: Inline 모듈이 있습니다 .

나는 테스트를 구성하는 데 특히 좋지 않으며 인라인 상태에서 유지 관리가 쉽고 쉽다는 것을 알았습니다.

제기 된 몇 가지 우려에 대한 대응 :

  • 인라인 테스트는 POD 섹션에 작성되므로 실제 코드의 일부가 아닙니다. 인터프리터에 의해 무시되므로 코드 팽창이 없습니다.
  • Vim 접기 를 사용 하여 테스트 섹션을 숨 깁니다. 당신이 보는 유일한 것은처럼 테스트되고있는 각 방법 위의 한 줄 +-- 33 lines: #test----입니다. 테스트 작업을하려는 경우 확장하면됩니다.
  • Test :: Inline 모듈은 테스트를 일반 TAP 호환 파일로 "컴파일"하여 기존 테스트와 공존 할 수 있습니다.

참고로 :


1

Erlang 2는 실제로 인라인 테스트를 지원합니다. 사용되지 않은 (예 : 변수에 할당되거나 전달 된) 코드의 부울 표현식은 자동으로 테스트로 처리되고 컴파일러에 의해 평가됩니다. 표현식이 false이면 코드가 컴파일되지 않습니다.


1

테스트를 분리하는 또 다른 이유는 실제 구현보다 테스트를 위해 추가 라이브러리 나 다른 라이브러리를 자주 사용하기 때문입니다. 테스트와 구현을 혼합하면 구현에서 테스트 라이브러리의 우발적 인 사용을 컴파일러가 잡을 수 없습니다.

또한 테스트는 테스트하는 구현 부분보다 더 많은 코드 행을 갖는 경향이 있으므로 모든 테스트 사이에서 구현을 찾는 데 어려움이 있습니다. :-)


0

사실이 아닙니다. 생산 코드가 특히 생산 루틴이 순수 할 때 생산 테스트 코드와 함께 단위 테스트를 배치하는 것이 훨씬 좋습니다.

예를 들어 .NET에서 개발하는 경우 테스트 코드를 프로덕션 어셈블리에 넣은 다음 Scalpel 을 사용 하여 배송 전에 제거 할 수 있습니다.

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