모든 방법을 테스트해야합니까?


61

그래서 오늘 저는 팀 테스트를 통해 유닛 테스트에 대해 이야기했습니다. 그가 내게 물었을 때 모든 것이 시작되었습니다. "이봐 요, 그 수업의 시험은 어디에 있습니까?" 전체 클래스는 관리자 (또는 그렇게 부르기를 선호하는 경우 서비스)였으며 거의 ​​모든 메소드는 단순히 DAO에 물건을 위임했기 때문에 다음과 비슷했습니다.

SomeClass getSomething(parameters) {
    return myDao.findSomethingBySomething(parameters);
}

논리가없는 (또는 적어도 논리와 같은 단순한 위임을 고려하지는 않지만) 대부분의 경우 유용한 보일러 플레이트 (레이어 분리 등)는 상용구입니다. 그리고 우리는 그것을 단위 테스트해야하는지 아닌지에 대해 다소 긴 토론을했습니다 (DAO를 완전히 단위 테스트했다고 언급 할 가치가 있다고 생각합니다). 그의 주요 주장은 TDD가 아니었고 누군가 가이 방법이 무엇인지 확인하기 위해 테스트를보고 싶을 수도 있고 (더 분명한 방법을 모르겠습니다) 미래에 누군가가 구현하고 새로운 논리를 추가하십시오 (이 경우 누군가가 단순히 그 논리 를 테스트해야한다고 생각합니다 ).

그래도 그렇게 생각했습니다. 가장 높은 테스트 범위 %를 위해 노력해야합니까? 아니면 단순히 예술을위한 예술입니까? 나는 단순히 다음과 같은 것들을 테스트하는 이유를 보지 못합니다.

  • 게터와 세터 (실제로 논리가없는 한)
  • "boilerplate"코드

분명히 그러한 방법 (모의가있는)에 대한 테스트는 1 분도 걸리지 않지만 모든 CI에 대해 여전히 시간이 낭비되고 밀리 초가 더 걸린 것 같습니다.

모든 (또는 가능한 한 많은) 코드 라인을 테스트해야하는 합리적 / 가연성 이유가 있습니까?


2
나는 아직도이 질문에 대해 생각하고 있지만 대답이 "아니오"라고 결정한 사람에 대한 이야기가 있습니다. 이안 쿠퍼 (Ian Cooper) : TDD, 모든 것이 잘못 되었다이 위대한 대화를 요약하려면, 새로운 방법이 아닌 외부에서 테스트하고 새로운 행동을 테스트해야 합니다.
Daniel Kaplan

이것은 정말 훌륭한 대화, 필견, 많은 사람들을위한 눈길을 끄는 대화, 나는 그것을 좋아합니다. 그러나 나는 대답이 "아니오"가 아니라고 생각합니다. "그렇지만 간접적으로"입니다. Ian Cooper는 6 각형 아키텍처 및 포트 조롱 / 스터 빙 테스트 기능 / 동작에 대해 설명합니다. 이 경우이 포트는 DAO이며이 "관리자 / 서비스"는이 클래스에 대한 개별 단위 테스트뿐만 아니라 일부 기능을 테스트하는 "단위 테스트"(완전히 동의하는 Ian Cooper 정의의 단위)로 테스트되었습니다. 이 관리자 / 서비스를 사용하는 도메인에서
AlfredoCasado


시스템에 어느 정도까지 영향을 미치며, 중간 수준에서 높은 수준의 안전 인증을 갖춘 시스템을 개발하는 경우 사소한
jk에

답변:


48

나는 켄트 벡 (Kent Beck)의 경험에 따라 간다 :

파손될 수있는 모든 것을 시험하십시오.

물론, 그것은 어느 정도 주관적입니다. 나에게, 위와 같은 사소한 게터 / 세터와 원 라이너는 그만한 가치가 없다. 그러나 다시 한 번, 대부분의 시간을 레거시 코드에 대한 단위 테스트를 작성하면서 멋진 그린 필드 TDD 프로젝트를 꿈꾸고 있습니다 ... 그러한 프로젝트에서는 규칙이 다릅니다. 레거시 코드를 사용하는 경우 주된 목표는 가능한 적은 노력으로 최대한 많은 근거를 다루는 것이므로 단위 테스트는 용어에 대한 지식이있는 경우 통합 테스트와 같이 더 높은 수준과 더 복잡한 경향이 있습니다. 그리고 전체 코드 범위를 0 %에서 올리려고 애 쓰고 있거나 25 % 이상 부딪 칠 때 단위 테스트 게터와 세터는 걱정할 필요가 없습니다.

그린 필드 TDD 프로젝트에서 OTOH의 경우 이러한 방법에 대한 테스트도 작성하는 것이 더 중요 할 수 있습니다. 특히 이미 테스트를 작성 했으므로 "이 한 줄은 전용 테스트의 가치가 있습니까?" 그리고 최소한 이러한 테스트는 작성하기가 쉽고 실행 속도가 빠르므로 큰 문제는 아닙니다.


아 나는 그 견적을 완전히 잊었다! 솔직히 말해서 이것을 나의 주요 주장으로 사용할 것 같아요-여기서 무엇을 깨뜨릴 수 있습니까? 별로. 깨질 수있는 유일한 방법은 메소드 호출이며 이것이 발생하면 실제로 나쁜 일이 발생했음을 의미합니다. 감사!
Zenzen

5
@ 젠젠 : "무엇을 깨뜨릴 수 있을까요? - 깨질 수 있습니다 . 작은 오타입니다. 아니면 누군가 코드를 추가합니다. 또는 의존성을 엉망으로 만듭니다. 나는 Beck이 당신의 주요 예제가 깨지기 쉬운 것으로 주장한다고 생각합니다. Getter와 Setter는 덜 복사 적이지만 붙여 넣기 오류가 발생하더라도 적습니다. 실제 질문은 테스트를 작성하기가 너무 사소한 경우에도 왜 존재 하는가입니다.
pdr

1
당신이 그것에 대해 이미 생각한 시간은 이미 시험을 작성했을 수 있습니다. 테스트를 작성하고 회색 영역으로 테스트를 작성하지 않을 때 떠나지 말고 더 깨진 창이 나타납니다.
kett_chup

1
필자의 일반적인 경험은 게터와 세터를 테스트하는 것이 장기적이지만 우선 순위가 낮 으면 다소 가치가 있다는 것입니다. 그 이유는 지금 버그를 발견 할 가능성이 "제로"인 이유 때문입니다. 다른 개발자가 3 개월 안에 무언가를 추가하지 않을 것 ( "단순한 if 문")이 깨질 가능성을 보장 할 수는 없습니다 . 단위 테스트를 실시하면 이에 대비할 수 있습니다. 동시에, 그렇게 빨리 어떤 것도 찾지 못할 것이기 때문에 우선 순위가 너무 높지는 않습니다.
dclements

7
깨질 수있는 모든 것을 맹목적으로 테스트하는 것은 의미가 없습니다. 고위험 부품을 먼저 테스트하는 전략이 필요합니다.
CodeART

12

단위 테스트에는 몇 가지 유형이 있습니다.

  • 상태 기반. 당신은 행동하고 물체의 상태에 대해 주장한다. 예를 들어 예금을합니다. 그런 다음 잔액이 증가했는지 확인합니다.
  • 반환 값을 기준으로합니다. 귀하는 반품 가치에 대해 행동하고 주장합니다.
  • 상호 작용 기반. 객체가 다른 객체를 호출했는지 확인합니다. 이것은 당신이 당신의 모범에서하고있는 것처럼 보입니다.

테스트를 먼저 작성했다면 데이터 액세스 계층을 호출 할 것으로 예상되는 것보다 더 의미가 있습니다. 처음에는 테스트가 실패합니다. 그런 다음 테스트를 통과하기 위해 프로덕션 코드를 작성합니다.

이상적으로는 논리 코드를 테스트해야하지만 상호 작용 (다른 개체를 호출하는 개체)도 마찬가지로 중요합니다. 네 경우에는

  • 전달 된 정확한 매개 변수를 사용하여 데이터 액세스 계층을 호출했는지 확인하십시오.
  • 한 번만 호출되었는지 확인하십시오.
  • 데이터 액세스 계층에서받은 내용을 정확하게 반환하는지 확인하십시오. 그렇지 않으면 null을 반환 할 수도 있습니다.

현재 거기에는 논리가 없지만 항상 그런 것은 아닙니다.

그러나이 방법에 논리가 없으며 그것이 동일하게 유지 될 것이라고 확신한다면 소비자로부터 직접 데이터 액세스 계층을 호출하는 것을 고려할 것입니다. 나머지 팀이 같은 페이지에있는 경우에만이 작업을 수행합니다. "이봐, 도메인 레이어를 무시하고 데이터 액세스 레이어를 직접 호출하는 것이 좋습니다"라고 말하여 팀에 잘못된 메시지를 보내지 않으려 고합니다.

이 방법에 대한 통합 테스트가 있다면 다른 구성 요소 테스트에도 집중할 것입니다. 그래도 견고한 통합 테스트를 한 회사는 아직 보지 못했습니다.

이 모든 것을 말하면서-나는 모든 것을 맹목적으로 테스트하지는 않을 것입니다. 핫스팟 (높은 복잡성과 높은 파괴 위험을 가진 구성 요소)을 설정합니다. 그런 다음 이러한 구성 요소에 집중합니다. 나머지 90 %가 시스템의 핵심 로직을 나타내며 복잡성으로 인해 단위 테스트에 포함되지 않는 경우 코드베이스의 90 %가 매우 간단하고 단위 테스트에 의해 다루어지는 코드베이스를 가질 필요는 없습니다.

마지막으로이 방법을 테스트하면 어떤 이점이 있습니까? 이것이 작동하지 않으면 어떤 의미가 있습니까? 그들은 치명적인가? 높은 코드 적용 범위를 얻으려고 노력하지 마십시오. 코드 범위는 우수한 단위 테스트 제품군의 제품이어야합니다. 예를 들어, 트리를 따라 이동하여이 방법의 100 % 적용 범위를 제공하는 하나의 테스트를 작성하거나 100 % 적용 범위를 제공하는 3 가지 단위 테스트를 작성할 수 있습니다. 차이점은 세 가지 테스트를 작성하여 트리를 걷는 것과는 대조적으로 에지 사례를 테스트한다는 것입니다.


왜 DAL이 한 번만 호출되었는지 확인 하시겠습니까?
Marjan Venema

9

소프트웨어의 품질을 생각하는 좋은 방법은 다음과 같습니다.

  1. 유형 검사는 문제의 일부를 처리합니다.
  2. 테스트는 나머지를 처리합니다

상용구 및 사소한 기능의 경우 작업을 수행하는 유형 검사에 의존 할 수 있으며 나머지는 테스트 사례가 필요합니다.


물론 유형 검사는 코드에서 특정 유형을 사용하고 컴파일 된 언어를 사용하거나 정적 분석 검사가 CI의 일부로 자주 실행되는지 확인하는 경우에만 작동합니다.
bdsl

6

제 생각에는 순환 복잡성은 매개 변수입니다. 메소드가 충분히 복잡하지 않은 경우 (예 : 게터 및 세터). 단위 테스트가 필요하지 않습니다. McCabe의 Cyclomatic Complexity 수준은 1보다 커야합니다. 또 다른 단어는 최소 1 개의 블록 설명이 있어야합니다.


일부 게터 나 세터는 부작용이 있으므로 (대부분의 경우 권장하지 않고 나쁜 습관으로 간주되지만) 소스 코드를 변경해도 영향을 줄 수 있습니다.
Andrzej Bobak

3

TDD와 함께 그렇습니다 (몇 가지 예외가 있음)

논란의 여지가 있지만, 나는이 질문에 '아니오'라고 대답하는 사람은 TDD의 기본 개념이 누락되었다고 주장합니다.

나를 위해, 대답은 울려 퍼지는이다 는 TDD를 수행합니다. 그렇지 않다면 그럴듯한 대답은 없습니다.

TDD의 DDD

TDD는 종종 주요 이점이 있다고 인용됩니다.

  • 방어
    • 코드가 변경 될 수 있지만 동작 은 변경 되지 않는지 확인하십시오 .
    • 이것은 리팩토링의 매우 중요한 관행을 허용합니다 .
    • 이 TDD를 얻거나 얻지 못합니다.
  • 디자인
    • 당신은 지정 이 작동하는 방법, 뭔가해야 할 일 구현하기 전에 그것을.
    • 이것은 종종보다 현명한 구현 결정을 의미 합니다.
  • 문서
    • 테스트 스위트는 사양 (요구 사항) 문서 로 사용되어야합니다 .
    • 그러한 목적으로 테스트를 사용한다는 것은 문서와 구현이 항상 일관된 상태임을 의미합니다. 하나의 변경은 다른 변경을 의미합니다. 별도의 워드 문서에서 요구 사항과 디자인을 유지하는 것과 비교하십시오.

구현과 별도의 책임

프로그래머로서, 속성을 중요하고 게터로 생각하고 일종의 오버 헤드로 세터를 생각하는 것은 대단히 유혹적입니다.

그러나 속성은 구현 세부 사항이며 세터와 게터는 실제로 프로그램을 작동시키는 계약 인터페이스입니다.

객체가 다음과 같이 철자를 작성하는 것이 훨씬 더 중요합니다.

클라이언트가 상태를 변경하도록 허용

클라이언트가 상태를 쿼리하도록 허용

그런 다음이 상태가 실제로 저장되는 방법 (속성이 가장 일반적이지만 유일한 방법은 아닙니다).

다음과 같은 테스트

(The Painter class) should store the provided colour

TDD 의 문서 부분에 중요합니다 .

테스트를 작성할 때 최종 구현이 사소한 (속성) 방어 이점 이 없다는 사실을 알 수 없습니다.

왕복 공학의 부족 ...

시스템 개발 세계의 주요 문제 중 하나는 왕복 공학 의 부족입니다. 1- 시스템의 개발 프로세스가 분리 된 하위 프로세스로 분할됩니다 (문서, 코드).

1 Brodie, Michael L. "John Mylopoulos : 개념적 모델링의 씨앗 봉제." 개념 모델링 : 기초 및 응용. Springer Berlin Heidelberg, 2009. 1-9.

... 그리고 TDD가이를 해결하는 방법

시스템 및 해당 코드의 스펙이 항상 일관되도록 TDD 의 문서 부분입니다.

먼저 디자인하고 나중에 구현

TDD 내에서 먼저 합격 합격 테스트를 작성한 다음 통과시키는 코드 만 작성하십시오.

높은 수준의 BDD 내에서 시나리오를 먼저 작성한 다음 통과시킵니다.

왜 세터와 게터를 제외해야합니까?

이론적으로 TDD 내에서 한 사람이 테스트를 작성하고 다른 사람이 테스트를 통과시키는 코드를 구현할 수 있습니다.

스스로에게 물어보십시오.

클래스에 대한 테스트를 작성하는 사람이 getter 및 setter를 언급해야합니다.

getter와 setter는 클래스에 대한 공용 인터페이스이므로 대답은 명백히 yes 이거나 객체의 상태를 설정하거나 쿼리 할 방법이 없습니다.

분명히 코드를 먼저 작성하면 답이 명확하지 않을 수 있습니다.

예외

이 규칙에는 명백한 구현 세부 사항이며 시스템 설계의 일부가 아닌 기능인 몇 가지 명백한 예외가 있습니다.

예를 들어 로컬 메소드 'B ()':

function A() {

    // B() will be called here    

    function B() {
        ...
    }
} 

또는 square()여기 개인 기능 :

class Something {
private:
    square() {...}
public:
    addAndSquare() {...}
    substractAndSquare() {...}
}

또는 public시스템 구성 요소 설계에서 철자를 필요 로하는 인터페이스의 일부가 아닌 다른 기능 .


1

철학적 quesion에 직면하면 운전 요구 사항으로 돌아갑니다.

합리적인 비용으로 합리적으로 신뢰할 수있는 소프트웨어를 생산하는 것이 목표입니까?

아니면 거의 비용에 관계없이 최고의 신뢰성을 가진 소프트웨어를 생산하는 것입니까?

요컨대, 품질과 개발 속도 / 비용의 두 가지 목표는 결함을 수정하는 것보다 테스트를 작성하는 데 더 적은 시간을 소비한다는 것입니다.

그러나 그 시점을 넘어서는 그렇지 않습니다. 예를 들어 한 달에 한 개발자 당 버그가보고 된 것은 그리 어렵지 않습니다. 이를 2 개월에 1 개로 줄이면 하루나 이틀 정도의 예산 만 책정되며 추가 테스트를 통해 결함률을 절반으로 줄일 수는 없습니다. 따라서 더 이상 단순한 승리 / 승리가 아닙니다. 고객의 결함 비용을 기준으로이를 정당화해야합니다.

이 비용은 다양 할 것입니다 (그리고 당신이 악하기를 원한다면 시장이나 소송을 통해 그 비용을 당신에게 다시 집행 할 수있는 능력). 당신은 악하기를 원하지 않기 때문에 그 비용을 모두 다시 계산합니다. 때로는 일부 테스트가 여전히 전 세계적으로 존재하기 때문에 세상을 더 나쁘게 만듭니다.

요컨대, 여객 정기 여객기 비행 소프트웨어와 동일한 표준을 사내 웹 사이트에 맹목적으로 적용하려고하면 사업이 중단되거나 투옥 될 수 있습니다.


0

이것에 대한 당신의 대답은 당신의 철학에 달려 있습니다 (시카고 대 런던이라고 믿습니까? 누군가 그것을 찾을 것이라고 확신합니다). 배심원은 여전히 ​​가장 시간 효율적인 접근 방식으로이 문제를 해결하지 못하고 있습니다.

어떤 접근법은 공용 인터페이스 만 테스트한다고 말하고 다른 접근법은 모든 함수에서 각 함수 호출의 순서를 테스트한다고 말합니다. 많은 거룩한 전쟁이 벌어졌습니다. 내 충고는 두 가지 접근법을 모두 시도하는 것입니다. 코드 단위를 선택하고 X, Y와 같은 코드를 선택하십시오. 몇 개월의 테스트 및 통합 후 돌아가서 어느 것이 더 적합한 지 확인하십시오.


0

까다로운 질문입니다.

엄밀히 말하면 나는 그것이 필요하지 않다고 말할 것입니다. 긍정적이고 부정적인 시나리오에서 비즈니스 요구 사항이 의도 한대로 작동하도록 BDD 스타일 단위 및 시스템 수준 테스트를 작성하는 것이 좋습니다.

그것은 당신의 방법이이 테스트 케이스들에 의해 다루지 않는다면 당신은 그것이 왜 존재하는지, 그리고 그것이 필요한지, 또는 문서 나 사용자 스토리에 반영되지 않은 코드에 숨겨진 요구 사항이 있는지 질문해야합니다. BDD 스타일 테스트 케이스로 인코딩해야합니다.

개인적으로 나는 줄당 기존 단위 테스트 적용 범위가 모든 코드 파일에 대해이 수준에 도달하고 파일이 발견되지 않도록 보장하기 위해 한 줄씩 적용 범위를 85-95 %로 유지하고 게이트 체크인을 메인 라인으로 유지하고 싶습니다.

최상의 테스트 관행을 따르고 있다고 가정하면 개발자는 단순히 코드 적용을 위해 연습하기 어려운 코드 또는 사소한 코드에 대한 추가 범위를 얻는 방법을 알아 내려고 시간을 낭비하지 않고도 많은 범위를 커버 할 수 있습니다.


-1

문제 자체는 문제입니다. 시스템의 모든 기능을 테스트하는 데 필요한 모든 "메 도스"또는 모든 "클래스"를 테스트 할 필요는 없습니다.

메소드와 클래스의 관점에서 생각하는 대신 기능 / 행동의 관점에 대한 핵심적인 생각. 물론 테스트 된 모든 코드, 최소한 코드 기반의 모든 코드는 하나 이상의 기능을 지원하기위한 방법입니다.

시나리오에서이 "관리자"클래스는 중복되거나 불필요합니다 (예 : "관리자"라는 단어가 포함 된 이름을 가진 모든 클래스처럼). 그렇지 않을 수도 있지만 구현 세부 사항처럼 보이지만이 클래스에는 단위가 필요하지 않습니다. 이 클래스에는 관련 비즈니스 로직이 없기 때문에 테스트하십시오. 아마도 일부 기능이 작동하려면이 클래스가 필요합니다.이 기능에 대한 테스트는이 클래스를 다루 므로이 클래스를 리팩터링하고 중요한 요소, 기능이 리팩토링 후에도 여전히 작동하는지 확인하는 테스트를 수행 할 수 있습니다.

메소드 클래스가 아닌 기능 / 동작을 생각하십시오.이를 충분히 반복 할 수는 없습니다.


-4

그래도 그렇게 생각했습니다. 가장 높은 테스트 범위 %를 위해 노력해야합니까?

예, 이상적으로는 100 %이지만 일부는 단위 테스트가 불가능합니다.

게터와 세터 (실제로 논리가없는 한)

게터 / 세터는 바보 입니다. 그냥 사용하지 마십시오. 대신 멤버 변수를 공개 섹션에 두십시오.

"boilerplate"코드

공통 코드를 가져 와서 단위 테스트하십시오. 그것은 그렇게 간단해야합니다.

모든 (또는 가능한 한 많은) 코드 라인을 테스트해야하는 합리적 / 가연성 이유가 있습니까?

그렇게하지 않으면 매우 명백한 버그를 놓칠 수 있습니다. 단위 테스트는 특정 종류의 버그를 잡을 수있는 안전한 그물과 같습니다. 가능한 한 많이 사용해야합니다.

그리고 마지막으로, 저는 사람들이 "간단한 코드"에 대한 단위 테스트를 작성하는 데 시간을 낭비하고 싶지 않았지만 나중에는 전혀 쓰지 않기로 결정했습니다. 결국 코드의 일부 는 진흙의 큰 공으로 변했습니다 .


한 가지만 바로 잡자. TDD / 쓰기 테스트를 사용하지 않는다는 의미는 아니다. 정반대. 테스트에서 내가 생각하지 못한 버그를 발견 할 수 있지만 여기서 테스트 할 것이 무엇입니까? 나는 단순히 그러한 방법이 "단위 테스트 불가능"방법 중 하나라고 생각합니다. Péter Török이 말했듯이 (Kent Beck을 인용) 깨질 수있는 물건을 테스트해야합니다. 여기서 무엇을 깨뜨릴 수 있습니까? 실제로는 많지 않습니다 (이 방법에는 간단한 위임 만 있습니다). 단위 테스트를 작성할 수는 있지만 DAO를 모의하고 테스트는 거의하지 않습니다. getter / setter에 관해서는 일부 프레임 워크가 필요합니다.
Zenzen

1
또한, "공통 코드를 꺼내서 단위 테스트하십시오. 그렇게 간단합니다." 그게 무슨 뜻 이니? (GUI와 DAO 사이의 서비스 계층에서) 서비스 클래스이며 전체 앱에 공통입니다. 실제로는 좀 더 일반적으로 만들 수는 없습니다 (일부 매개 변수를 수락하고 DAO에서 특정 메소드를 호출하기 때문에). GUI가 DAO를 직접 호출하지 않도록 응용 프로그램의 계층 구조를 고수하는 것이 유일한 이유입니다.
Zenzen

20
-1에 대해 "게터 / 세터는 바보 - 단지 그들을 사용하지 않는 대신, 공공 부분에 멤버 변수를 넣어.." -아주 잘못 됐어 이것은 SO에 대해 여러 번 논의되었습니다 . 어디서나 퍼블릭 필드를 사용하는 것이 어디서나 게터와 세터를 사용하는 것보다 실제로 더 나쁩니다.
Péter Török 2012 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.