TDD 테스트에서 테스트가 필요한 새로운 기능이 공개되면 어떻게해야합니까?


13

테스트를 작성하고 테스트를 통과해야 할 시점에 도달하면 자체 기능으로 분리해야하는 추가 기능이 필요하다는 것을 알고 있습니까? 이 새로운 기능도 테스트해야하지만 TDD주기는 테스트 실패, 리팩토링을한다고 말합니다. 테스트를 통과하려고하는 단계에 있다면 구현에 필요한 새로운 기능을 테스트하기 위해 실패한 테스트를 시작하지 않아야합니다.

예를 들어, WillCollideWith ( LineSegment ) 함수를 가진 포인트 클래스를 작성하고 있습니다 .

public class Point {
    // Point data and constructor ...

    public bool CollidesWithLine(LineSegment lineSegment) {
        Vector PointEndOfMovement = new Vector(Position.X + Velocity.X,
                                               Position.Y + Velocity.Y);
        LineSegment pointPath = new LineSegment(Position, PointEndOfMovement);
        if (lineSegment.Intersects(pointPath)) return true;
        return false;
    }
}

LineSegment.Intersects ( LineSegment ) 함수 가 필요하다는 것을 깨달았을 때 CollidesWithLine에 대한 테스트를 작성했습니다 . 그러나이 새로운 기능을 만들기 위해 테스트주기에서 수행중인 작업을 중단해야합니까? 그것은 "빨강, 녹색, 리 팩터"원칙을 어기는 것 같습니다.

lineSegments가 CollidesWithLine 함수 내부에서 교차한다는 것을 감지하고 작동 한 후 리팩터링 하는 코드를 작성해야합니까 ? LineSegment 에서 데이터에 액세스 할 수 있기 때문에이 경우에는 효과 가 있지만 그러한 종류의 데이터가 개인 정보 인 경우에는 어떻습니까?

답변:


14

테스트와 최근 코드를 주석 처리하거나 (또는 ​​숨겨두면) 실제로 사이클 시작으로 시계를 되돌릴 수 있습니다. 그런 다음 LineSegment.Intersects(LineSegment)테스트 / 코드 / 리 팩터로 시작하십시오 . 완료되면 이전 테스트 / 코드의 주석 처리를 제거하거나 숨겨 놓으십시오.


이것을 무시하고 나중에 다시 오는 것과 어떻게 다른가요?
Joshua Harris

1
작은 세부 사항 : 보고서에 추가 "무시"테스트가 없으며 숨김을 사용하면 코드가 '깨끗한'경우와 구별되지 않습니다.
Javier

숨김이란 무엇입니까? 버전 관리와 비슷합니까?
Joshua Harris

1
일부 VCS는이 기능을 기능 (적어도 Git 및 Fossil)으로 구현합니다. 변경 사항을 제거 할 수 있지만 나중에 다시 적용하기 위해 저장하십시오. diff를 저장하고 마지막 상태로 되돌리려면 수동으로하기가 어렵지 않습니다. 나중에 diff를 다시 적용하고 계속하십시오.
Javier

6

TDD주기에서 :

"테스트 패스 만들기"단계에서는 테스트 패스를 만드는 가장 간단한 구현 을 작성해야합니다 . 테스트 패스를 만들기 위해 누락 된 로직을 처리하기 위해 새 공동 작업자를 만들어서 포인트 클래스에 테스트 패스를 만들기에는 너무 많은 작업이 포함되었을 수 있습니다. 그것이 문제가있는 곳입니다. 통과하려고했던 시험이 너무 큰 단계 라고 생각합니다 . 따라서 문제는 테스트 자체에 있다고 생각합니다. 해당 테스트를 삭제 / 코멘트 하고 LineSegment.Intersects (LineSegment) 부분을 도입하지 않고 아기 발걸음 을 내딛을 수있는 더 간단한 테스트를 찾아야합니다. 테스트를 통과 한 리팩터링이 새로운 로직을 LineSegment.Intersects (LineSegment) 메소드로 이동하여 코드 (여기서는 SRP 원칙을 적용 할 것입니다.) 동작을 변경하지 않고 코드를 이동했기 때문에 테스트는 여전히 통과합니다.

현재 디자인 솔루션

그러나 나에게있어, 여기서 더 심오한 설계 문제는 단일 책임 원칙을 위반한다는 것 입니다. 포인트의 역할은 .... 포인트가되는 것입니다. 포인트가되는 현명함은 없으며 x와 y 값입니다. 포인트는 값 유형 입니다. 이것은 세그먼트와 동일합니다. 세그먼트는 두 점으로 구성된 값 유형입니다. 예를 들어 포인트 위치에 따라 길이를 계산하기 위해 약간의 "스마트 니스"를 포함 할 수 있습니다. 하지만 그게 다야.

이제 포인트와 세그먼트가 충돌하는지 여부를 결정하는 것은 전적으로 자체 책임입니다. 그리고 포인트 나 세그먼트가 스스로 처리하기에는 너무 많은 작업입니다. 포인트는 세그먼트에 대해 알 수 있기 때문에 Point 클래스에 속할 수 없습니다. 세그먼트는 이미 세그먼트 내의 포인트를 관리하고 세그먼트 자체의 길이를 계산할 책임이 있기 때문에 세그먼트에 속할 수 없습니다.

따라서이 책임은 다음과 같은 메소드를 갖는 "PointSegmentCollisionDetector"와 같은 다른 클래스가 소유해야합니다.

bool AreInCollision (포인트 p, 세그먼트 s)

그리고 그것은 당신이 Points and Segments에서 별도로 테스트 할 것입니다.

이 디자인의 좋은 점은 이제 충돌 감지기를 다르게 구현할 수 있다는 것입니다. 따라서 예를 들어 런타임에 충돌 감지 방법을 전환하여 게임 엔진을 벤치마킹하는 것이 쉽습니다 (게임을 작성한다고 가정합니다). 또는 다른 충돌 감지 전략 사이에서 런타임에 시각적 확인 / 실험을 수행합니다.

현재이 논리를 포인트 클래스에 넣으면 포인트 클래스에 대해 너무 많은 책임을지고 있습니다.

이해가 되길 바랍니다.


당신은 내가 변화의 너무 큰 시험하려고하는 것을 옳다하고 난 당신이 바로 충돌 클래스로 그를 오토넷에 대해 생각하지만, 그 날 당신이 나를 도울 수있을 것으로 완전히 새로운 질문을한다 : 해야 I를 메소드가 비슷할 때 인터페이스를 사용합니까? .
Joshua Harris

2

TDD 방식으로 수행하는 가장 쉬운 방법은 LineSegment에 대한 인터페이스를 추출하고 인터페이스에서 사용하도록 메소드 매개 변수를 변경하는 것입니다. 그런 다음 입력 라인 세그먼트를 조롱하고 Intersect 메소드를 독립적으로 코딩 / 테스트 할 수 있습니다.


1
나는 그것이 가장 많이 듣는 TDD 방법이라는 것을 알고 있지만 ILineSegment는 의미가 없습니다. 외부 리소스 또는 여러 형태로 제공 될 수있는 것을 인터페이스하는 것은 한 가지 일이지만, 어떤 기능을 라인 세그먼트 이외의 다른 것에 연결해야하는 이유는 알 수 없습니다.
Joshua Harris

0

jUnit4를 사용하면 @Ignore연기하려는 테스트에 주석을 사용할 수 있습니다 .

연기하려는 각 방법에 주석을 추가하고 필요한 기능에 대한 테스트를 계속 작성하십시오. 나중에 이전 테스트 사례를 리팩토링하려면 다시 원을 그리십시오.

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