리팩토링-모든 테스트가 통과되는 한 코드를 간단히 다시 작성하는 것이 적절합니까?


9

최근에 RailsConf 2014에서 "All the Little Things" 를 보았습니다 .

def tick
    if @name != 'Aged Brie' && @name != 'Backstage passes to a TAFKAL80ETC concert'
        if @quality > 0
            if @name != 'Sulfuras, Hand of Ragnaros'
                @quality -= 1
            end
        end
    else
        ...
    end
    ...
end

첫 번째 단계는 함수를 여러 개의 작은 함수로 나누는 것입니다.

def tick
    case name
    when 'Aged Brie'
        return brie_tick
    ...
    end
end

def brie_tick
    @days_remaining -= 1
    return if quality >= 50

    @quality += 1
    @quality += 1 if @days_remaining <= 0
end

내가 흥미로 웠던 것은이 작은 함수들이 작성되는 방식이었습니다. brie_tick예를 들어, 원래 tick함수 의 관련 부분을 추출하여 작성된 것이 아니라 test_brie_*단위 테스트를 참조하여 처음부터 작성되었습니다 . 이러한 모든 단위 테스트가 통과되면 brie_tick완료된 것으로 간주됩니다. 모든 작은 기능이 완료되면 원래의 모 놀리 식 tick기능이 삭제되었습니다.

안타깝게도 발표자는이 접근 방식으로 인해 네 가지 *_tick기능 중 세 가지 기능이 잘못되었다는 사실을 알지 못했습니다 (다른 기능은 비어 있음). *_tick기능 의 동작 이 원래 tick기능 의 동작 과 다른 경우가 있습니다. 예를 들어, @days_remaining <= 0brie_tick있어야합니다 < 0- 그래서 brie_tick호출 할 때 제대로 작동하지 않습니다 days_remaining == 1quality < 50.

여기서 무엇이 잘못 되었습니까? 이러한 특별한 경우에 대한 테스트가 없었기 때문에 이것이 테스트 실패입니까? 또는 리팩토링 실패-코드를 처음부터 다시 작성하지 않고 단계별로 변환해야합니까?


2
질문이 확실하지 않습니다. 물론 코드를 다시 작성해도됩니다. " 코드 를 간단히 다시 작성해도 괜찮습니다"라는 말의 의미가 무엇인지 잘 모르겠습니다 . "많이 생각하지 않고 코드를 다시 작성해도 괜찮습니다."라고 묻는다면 그 방법으로 코드 를 작성 하는 것이 좋지 않은 것처럼 대답은 '아니오' 입니다.
John Wu

이는 주로 성공 사례를 테스트하는 데 주로 중점을 둔 테스트 계획과 오류 사례 또는 하위 사용 사례를 다루는 데 거의 또는 전혀 적용되지 않기 때문에 발생합니다. 따라서 주로 적용 범위가 누출됩니다. 테스트 누출.
Laiv

@JohnWu-리팩토링은 일반적으로 단순히 코드를 다시 작성하는 것이 아니라 소스 코드 ( "추출 방법"등)에 대한 일련의 작은 변환으로 수행된다는 인상을 받았습니다. 링크 된 프리젠 테이션에서 수행 된 기존 코드 확인).
user200783

@JohnWu-처음부터 다시 작성하는 것이 허용 가능한 리팩토링 기술입니까? 그렇지 않다면 리팩토링에 대한 잘 알려진 프레젠테이션이 그 접근 방식을 취하는 것이 실망 스럽습니다. 허용되는 경우 의도하지 않은 행동의 변화는 누락 된 테스트에서 비난받을 수 있습니다. 그러나 테스트가 모든 가능한 경우를 커버한다고 확신 할 수있는 방법이 있습니까?
user200783

@ User200783 더 큰 질문입니다. (테스트가 포괄적인지 어떻게 확인합니까?) 실용적으로 변경하기 전에 코드 적용 범위 보고서를 실행하고 그렇지 않은 코드 영역을 신중하게 검사합니다. 개발 팀이 논리를 다시 작성할 때이를 염두에두고 연습하십시오.
John Wu

답변:


11

이러한 특별한 경우에 대한 테스트가 없었기 때문에 이것이 테스트 실패입니까? 또는 리팩토링 실패-코드를 처음부터 다시 작성하지 않고 단계별로 변환해야합니까?

양자 모두. Fowlers 원본 서적 의 표준 단계 만 사용하여 리팩토링 하는 것은 다시 작성하는 것보다 오류가 덜 발생하기 때문에 종종 이러한 종류의 베이비 단계 만 사용하는 것이 좋습니다. 모든 엣지 케이스에 대한 단위 테스트가없고 환경에서 자동 리팩토링을 제공하지 않더라도 "변수 설명 소개"또는 "추출 기능"과 같은 단일 코드 변경으로 인해 함수를 완전히 다시 작성하는 것보다 기존 코드

그러나 때로는 코드 섹션을 다시 작성해야 할 필요가 있습니다. 이 경우 더 나은 테스트가 필요합니다.

리팩토링 도구를 사용하는 경우에도 더 작거나 큰 단계를 적용하지 않고도 코드를 변경할 때 오류가 발생할 위험이 항상 있습니다. 리팩토링에 항상 테스트가 필요한 이유입니다. 테스트는 버그의 가능성을 줄일 수는 있지만 그 부재를 증명할 수는 없습니다. 그럼에도 불구하고 코드 및 브랜치 커버리지와 같은 기술을 사용하면 높은 신뢰 수준을 얻을 수 있으며 코드 섹션을 다시 쓰는 경우에는 종종 그러한 기술을 적용 할 가치가 있습니다.


1
고마워요. 따라서 바람직하지 않은 행동 변화에 대한 궁극적 인 해결책이 포괄적 인 테스트를 수행하는 것이라면 테스트가 가능한 모든 에지 사례를 포함한다고 확신 할 수있는 방법이 있습니까? 예를 들어 및 로 설정하여 brie_tick테스트하여 문제가되는 @days_remaining == 1사례를 테스트하지 않으면 서 100 % 적용 범위를 가질 수 있습니다 . @days_remaining10-10
user200783

2
가능한 모든 입력으로 테스트하는 것이 불가능하기 때문에 테스트가 모든 가능한 경우를 포괄한다고 절대 확신 할 수 없습니다. 그러나 시험에서 더 많은 확신을 얻는 방법은 많이 있습니다. 돌연변이 테스트를 살펴볼 수 있는데, 이는 테스트의 효과를 테스트하는 방법입니다.
bdsl

1
이 경우, 테스트를 개발하는 동안 누락 된 브랜치가 코드 커버리지 도구로 잡힐 수있었습니다.
cbojar

2

여기서 무엇이 잘못 되었습니까? 이러한 특별한 경우에 대한 테스트가 없었기 때문에 이것이 테스트 실패입니까? 또는 리팩토링 실패-코드를 처음부터 다시 작성하지 않고 단계별로 변환해야합니까?

레거시 코드로 작업하기가 정말 어려운 것 중 하나는 현재 동작에 대한 완전한 이해를 얻는 것입니다.

모든 동작을 제한하는 테스트가없는 레거시 코드는 일반적인 패턴입니다. 제약이없는 행동이 자유 변수라는 것을 의미합니까? 또는 불특정 한 요구 사항?

대화에서 :

이제 이것은 리팩토링의 정의에 따른 실제 리팩토링입니다. 이 코드를 리팩토링하겠습니다. 나는 행동을 바꾸지 않고 배열을 바꿀 것입니다.

이것은보다 보수적 인 접근법입니다. 요구 사항이 지정되지 않은 경우 테스트에서 기존 논리를 모두 캡처하지 못하면 진행 방법에 매우주의해야합니다.

테스트가 시스템의 동작을 부적절하게 설명하는 경우 "테스트 실패"가 있다고 확신 할 수 있습니다. 그리고 나는 그것이 공평하다고 생각하지만 실제로는 유용하지 않습니다. 이것은 야생에서 흔히 발생하는 문제 입니다.

또는 리팩토링 실패-코드를 처음부터 다시 작성하지 않고 단계별로 변환해야합니까?

문제는되지 상당히 변형에 단계별 있었어야; 그러나 오류율이 높기 때문에 리팩토링 도구 (안내 자동화 대신 휴먼 키보드 운영자?)의 선택이 테스트 범위와 잘 맞지 않았다.

이 해결되었습니다 수 더 높은 신뢰성과 리팩토링 도구를 사용하거나 시스템의 제약을 개선하기 위해 테스트의 넓은 배터리를 도입.

그래서 나는 당신의 연결이 잘못 선택되었다고 생각합니다. AND아닙니다 OR.


2

리팩토링은 코드의 외부에서 보이는 행동을 변경해서는 안됩니다. 그것이 목표입니다.

단위 테스트에 실패하면 동작이 변경되었음을 나타냅니다. 그러나 단위 테스트를 통과하는 것은 결코 목표가 아닙니다. 목표 달성에 다소 도움이됩니다. 리팩토링이 외부에서 보이는 동작을 변경하고 모든 단위 테스트가 통과하면 리팩토링에 실패한 것입니다.

이 경우 작업 단위 테스트는 잘못된 성공 느낌 만 줄뿐입니다. 그러나 무엇이 잘못 되었습니까? 두 가지 : 리팩토링은 부주의했고 단위 테스트는 그리 좋지 않았습니다.


1

"올바른"을 "테스트 통과" 로 정의 하면 정의 되지 않은 동작을 변경하는 것은 잘못이 아닙니다.

특정 모서리 동작 정의 해야하는 경우 테스트를 추가하십시오 (그렇지 않은 경우). 당신이 정말로 pedantic이라면, 당신은 그 행동이 무엇인지 신경 쓰지 않는다는true 것을 문서화 하기 위해 그 에지 케이스에 언제 있는지 검사하는 테스트를 작성할 수 있습니다 .

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