경로 적용 범위가 모든 버그를 찾도록 보장합니까?


64

프로그램의 모든 경로를 테스트하면 모든 버그를 찾을 수 있습니까?

그렇지 않다면 왜 안됩니까? 가능한 모든 프로그램 흐름 조합을 통해 어떻게 문제를 발견 할 수 있습니까?

나는 "모든 버그"를 찾을 수 있다고 주저하지만, 아마도 경로 범위가 실용적이지 않기 때문에 (조합 적이므로) 경험이 없기 때문일 수 있습니까?

참고 : 이 기사 는 내가 생각할 때 적용 범위 유형에 대한 간략한 요약을 제공합니다.


33
이것은 정지 문제 와 같습니다 .

31
거기에 있어야 할 코드가 없다면 어떨까요?
RemcoGerlich

6
@ 눈사람 : 아니오, 그렇지 않습니다. 모든 프로그램의 정지 문제를 해결할 수는 없지만 많은 특정 프로그램의 경우 해결할 수 있습니다. 이러한 프로그램의 경우 모든 코드 경로를 유한 한 (아마도 긴 시간) 열거 할 수 있습니다.
Jørgen Fogh 2016 년

3
@ JørgenFogh 그러나 어떤 프로그램 에서 버그를 찾으려고 할 때 , 프로그램이 멈출 지 여부는 선험적으로 알려지지 않았습니까? "경로 범위를 통해 프로그램의 모든 버그를 찾는" 일반적인 방법 에 대한이 질문이 아닌가 ? 어떤 경우에, "프로그램 정지 여부 찾기"와 유사하지 않습니까?
Andres F.

1
@AndresF. 작성된 언어하위 집합이 중단되지 않는 프로그램을 표현할 수있는 경우 프로그램이 중지되는지 여부는 알 수 없습니다 . 무한 루프 / 재귀 / setjmp 등을 사용하지 않고 C로 작성하거나 Coq 또는 ESSL로 프로그램을 작성하면 정지해야하며 모든 경로를 추적 할 수 있습니다. (
Turling

답변:


128

프로그램의 모든 경로를 테스트하면 모든 버그를 찾을 수 있습니까?

아니

그렇지 않다면 왜 안됩니까? 가능한 모든 프로그램 흐름 조합을 통해 어떻게 문제를 발견 할 수 있습니까?

모든 가능한 경로 를 테스트하더라도 모든 가능한 또는 가능한 모든 값의 조합으로 경로 를 테스트 하지 않았기 때문 입니다. 예를 들어 (의사 코드) :

def Add(x as Int32, y as Int32) as Int32:
   return x + y

Test.Assert(Add(2, 2) == 4) //100% test coverage
Add(MAXINT, 5) //Throws an exception, despite 100% test coverage

프로그램 테스트가 버그의 존재를 설득력있게 보여줄 수는 있지만, 그 부재를 보여줄 수는 없다고 지적 된 지 20 년이 지났습니다 . 이 잘 알려진 발언을 솔직하게 인용 한 후, 소프트웨어 엔지니어는 요크의 연금술사와 마찬가지로 오늘의 순서로 돌아가서 그의 크리 소 코스 믹 정화를 계속 개선 한 테스트 전략을 계속 개선합니다.

- EW 데이 크 스트라 (강조는 추가 된 1988 년에 작성된 그것은 이상 2 년 지금은 상당히있었습니다..)


7
@digitgopher : 프로그램에 입력이 없으면 어떤 유용한 기능을 수행합니까?
메이슨 휠러

34
통합 테스트 누락, 테스트 버그, 종속성 버그, 빌드 / 배포 시스템 버그 또는 원래 사양 / 요구 사항의 버그가있을 수도 있습니다. 모든 버그 찾기를 보장 할 수는 없습니다 .
Ixrec

11
@Ixrec : SQLite는 꽤 용감한 노력을합니다! 그러나 그것이 얼마나 큰 노력인지보십시오! 큰 코드베이스에는 적합하지 않습니다.
메이슨 휠러

13
뿐만 아니라 당신은 그 모든 가능한 값이나 조합을 테스트하지 것입니다, 당신은 경쟁 조건을 노출하거나 실제로 테스트 그것을보고 실패 할 것 교착 입력 할 수있는 몇 가지있는 모든 상대 타이밍, 시험하지 않았다 아무것도 . 실패조차하지 않습니다!
Idonotexist Idonotexist

14
(힘 입어 내 기억 이와 같은 글 ) 다 익스트라가 좋은 프로그래밍 관행, 프로그램 (모든 조건에서) 정확한 증거가 첫번째 장소에있는 프로그램의 개발의 중요한 부분이되어야한다는 생각이다. 그런 관점에서 보면 테스트 연금술과 같습니다. 과장보다는 오히려 이것은 매우 강력한 언어로 표현 된 매우 강력한 의견이라고 생각합니다.
David K

71

뿐만 아니라 메이슨의 대답 , 또 다른 문제가있다 : 범위가 않습니다 하지 코드가 테스트되었습니다 무엇을 말해,이 코드가 무엇인지를 알려줍니다 실행 .

100 % 경로 적용 범위를 가진 테스트 스위트가 있다고 가정하십시오. 이제 모든 어설 션을 제거하고 테스트 스위트를 다시 실행하십시오. Voilà, 테스트 스위트는 여전히 100 % 경로 커버리지를 가지고 있지만 전혀 테스트하지 않습니다.


2
테스트 된 코드를 호출 할 때 (테스트의 매개 변수와 함께) 예외가 없는지 확인할 수 있습니다. 이것은 아무것도 아닌 것 이상입니다.
Paŭlo Ebermann

7
@ PaŭloEbermann 동의합니다. 그러나 "모든 버그를 찾는 것"보다 훨씬 작습니다.;)
Andres F.

1
@ PaŭloEbermann : 예외는 코드 경로입니다. 코드가 던져 질 수 있지만 특정 테스트 데이터로 던져지지 않으면 테스트는 100 % 경로 적용 범위를 달성하지 못합니다. 이것은 오류 처리 메커니즘으로서 예외에 국한되지 않습니다. Visual Basic ON ERROR GOTO도 C와 마찬가지로 경로 if(errno)입니다.
MSalters

1
@MSalters 입력에 관계없이 (사양에 따라) 예외를 throw하지 않아야하는 코드에 대해 이야기하고 있습니다. 그것이 던져지면 그것은 버그 일 것입니다. 물론 예외를 발생 시키도록 지정된 코드가 있으면 테스트해야합니다. 물론 Jörg가 말했듯이 코드에서 예외가 발생하지 않는지 확인하는 것만으로도 예외가 발생하지 않는 코드의 경우에도 올바른 동작을 수행하기에 충분하지 않습니다. 널 포인터 역 참조 또는 0으로 나누기와 같은-보이는 코드 경로. 귀하의 경로 적용 도구가 그러한 것들을 포착합니까?
Paŭlo Ebermann

2
이 대답은 그것을 못 박는 다. 나는 주장을 더 나아가서 이것으로 인해 경로 적용 범위가 단일 버그조차도 보장하지 않는다고 말합니다. 최소한 변경 사항이 감지 될 수 있음을 보장 할 수있는 메트릭이 있지만, 돌연변이 테스트를 통해 실제로 코드의 일부 (일부) 수정 이 감지 수 있습니다.
eis

34

다음은 사물을 반올림하는 더 간단한 예입니다. 다음 정렬 알고리즘 (Java)을 고려하십시오.

int[] sort(int[] x) { return new int[] { x[0] }; }

이제 테스트 해 보자.

sort(new int[] { 0xCAFEBABE });

이제 (A)이 특정 호출이 sort올바른 결과 를 반환하고 (B) 모든 코드 경로가이 테스트에 포함되었다는 것을 고려하십시오.

그러나 분명히 프로그램은 실제로 정렬되지 않습니다.

모든 코드 경로의 적용 범위가 프로그램에 버그가 없음을 보장하기에는 충분하지 않습니다.


12

abs숫자의 절대 값을 반환 하는 함수를 고려하십시오 . 다음은 테스트입니다 (Python, 테스트 프레임 워크를 상상해보십시오).

def test_abs_of_neg_number_returns_positive():
    assert abs(-3) == 3

이 구현은 정확하지만 코드 적용 범위는 60 %입니다.

def abs(x):
    if x < 0:
        return -x
    else:
        return x

이 구현은 잘못되었지만 100 % 코드 적용을 얻습니다.

def abs(x):
    return -x

2
: 여기에 (비 linebroken 파이썬을 용서하시기 바랍니다) 테스트를 통과 또 다른 구현 def abs(x): if x == -3: return 3 else: return 0당신은 아마도 생략하다 수있는 else: return 0부분을 100 % 적용 범위를 얻을 수 있지만, 함수는 단위 테스트를 통과 않는 경우에도 본질적으로 쓸모가있을 것입니다.
CVn

7

메이슨의 대답에 또 다른 추가 사항 , 프로그램의 동작은 런타임 환경에 따라 달라질 수 있습니다.

다음 코드에는 Use-After-Free가 포함되어 있습니다.

int main(void)
{
    int* a = malloc(sizeof(a));
    int* b = a;
    *a = 0;
    free(a);
    *b = 12; /* UAF */
    return 0;
}

이 코드는 정의되지 않은 동작이며, 구성 (release | debug), OS 및 컴파일러에 따라 다른 동작이 발생합니다. 경로 적용 범위가 UAF를 찾도록 보장하지는 않지만 테스트 스위트는 일반적으로 구성에 따라 가능한 다양한 UAF 동작을 다루지 않습니다.

다른 참고로, 경로 적용 범위가 모든 버그를 찾도록 보장하더라도 실제로는 모든 프로그램에서 달성 할 수는 없습니다. 다음 중 하나를 고려하십시오.

int main(int a, int b)
{
    if (a != b) {
        if (cryptohash(a) == cryptohash(b)) {
            return ERROR;
        }
    }
    return 0;
} 

테스트 슈트가 이에 대한 모든 경로를 생성 할 수 있다면 축하합니다.


충분히 작은 정수에 대 한 쉬운 :)
CodeInChaos

에 대해 아무 것도 몰라도 cryptohash"충분히 작은"것이 무엇인지 말하기는 조금 어렵습니다. 수퍼 계산기에서 완료하는 데 이틀이 걸릴 수 있습니다. 그러나 네, int조금 밝혀 질 것 short입니다.
dureuill 2016 년

32 비트 정수 및 일반적인 암호화 해시 (SHA2, SHA3 등) 컴퓨팅에서는 상당히 저렴해야합니다. 몇 초 정도
코드 InChaos

7

다른 답변에서 테스트에서 100 % 코드 적용 범위가 100 % 코드 정확성을 의미하지는 않으며 테스트를 통해 찾을 수있는 모든 버그가 발견 될 것입니다 (테스트가 포착 할 수없는 버그는 염두에 두지 마십시오).

이 질문에 대답하는 또 다른 방법은 연습 방법입니다.

실제 세계에는 실제로 컴퓨터에는 100 % 적용 범위를 제공하는 테스트 세트를 사용하여 개발되었지만 더 나은 테스트로 식별 할 수있는 버그를 포함하여 여전히 버그가있는 많은 소프트웨어가 있습니다.

따라서 다음과 같은 질문이 있습니다.

코드 적용 툴의 요점은 무엇입니까?

코드 범위 도구는 테스트를 소홀히 한 영역을 식별하는 데 도움이됩니다. 그것은 괜찮을 수도 있습니다 (테스트하지 않아도 코드는 명백히 정확합니다) 해결하기가 불가능하거나 (어떤 이유로 경로를 칠 수없는 경우), 또는 현재 또는 향후 수정 후에 큰 버그가있는 위치 일 수 있습니다.

어떤 방법으로 철자 검사는 비교할 수 있습니다 : 어떤 것은 철자 검사를 "통과"하고 사전에있는 단어와 일치하는 방식으로 철자가 틀릴 수 있습니다. 또는 올바른 단어가 사전에 없기 때문에 "실패"할 수 있습니다. 또는 그것은 말도 안되고 지나칠 수 있습니다. 맞춤법 검사는 교정에서 누락되었을 수있는 장소를 식별하는 데 도움이되는 도구이지만, 완벽하고 정확한 교정을 보장 할 수없는 것처럼 코드 범위는 완전하고 정확한 테스트를 보장 할 수 없습니다.

물론 맞춤법 검사를 사용하는 잘못된 방법은 제안 된 모든 제안과 함께 진행되는 것으로 유명합니다. 따라서 ewe가 대출을 떠난 경우 더킹 현상이 악화됩니다.

코드 적용 범위를 사용하면 특히 98 %에 가까운 경우 나머지 경로에 도달하도록 케이스를 채우려는 유혹을받을 수 있습니다.

그것은 맞춤법 검사와 함께 오른쪽 단어가 날씨이거나 모든 단어가 적절한 단어라는 것을 의미합니다. 결과는 더킹 혼란입니다.

그러나 커버되지 않은 경로가 실제로 필요한 테스트를 고려하면 코드 커버리지 도구가 작업을 수행했을 것입니다. 정확성을 약속하는 것이 아니라 수행해야 할 작업 중 일부 를 지적 합니다.


+1이 답변은 건설적이고 적용 범위의 이점을 언급하기 때문에이 답변이 마음에 듭니다.
Andres F.

4

경로 적용 범위는 필요한 모든 기능이 구현되었는지 여부를 알려줄 수 없습니다. 기능을 남겨 두는 것은 버그이지만 경로 범위는 감지하지 않습니다.


1
나는 그것이 버그의 정의에 달려 있다고 생각합니다. 누락 된 기능이나 기능을 버그로 생각해서는 안됩니다.
eis

@eis-실제로 문서가 X가 아니라고 말하는 문서가있는 제품에 문제가 보이지 않습니까? 그것은 "버그"에 대한 다소 좁은 정의입니다. 볼랜드의 C ++ 제품 라인에 대한 QA를 관리했을 때 우리는 관대하지 않았습니다.
피트 베커

그 이유 내용은 해당 구현하지 않은 경우는 X를하지 말 것 보지 않는다
EIS

@eis-원래 디자인이 기능 X를 요구했다면 문서에서 기능 X를 설명 할 수 있습니다.
피트 베커

경로 범위는 블랙 박스가 아닌 화이트 박스 테스트 입니다. 화이트 박스 테스트는 누락 된 기능을 포착 할 수 없습니다.
피트 베커

4

이 문제의 일부는 100 % 적용 범위만으로도 단일 실행 후 코드가 올바르게 작동한다는 것만 보장한다는 것 입니다. 메모리 누수와 같은 일부 버그는 단일 실행 후 명백하지 않거나 문제를 일으킬 수 있지만 시간이 지남에 따라 응용 프로그램에 문제가 발생할 수 있습니다.

예를 들어, 데이터베이스에 연결하는 응용 프로그램이 있다고 가정하십시오. 아마도 한 가지 방법으로 프로그래머는 쿼리가 완료되면 데이터베이스에 대한 연결을 닫는 것을 잊어 버립니다. 이 방법에 대해 몇 가지 테스트를 실행하고 해당 기능의 오류를 찾을 수는 없지만,이 특정 방법이 연결을 닫지 않았고 열린 연결이 있어야하기 때문에 데이터베이스 서버가 사용 가능한 연결이없는 시나리오로 실행될 수 있습니다. 이제 시간이 초과되었습니다.


그것이 문제의 일부라고 동의했지만 실제 문제는 그보다 더 근본적입니다. 메모리가 무한하고 동시성이없는 이론적 인 컴퓨터에서도 100 % 테스트 범위는 버그가 없음을 의미하지 않습니다. 이것에 대한 간단한 예는 여기에 답변에 많이 있지만 여기에 또 다른 것이 있습니다 : 내 프로그램이 있다면 times_two(x) = x + 2, 이것은 테스트 스위트에 의해 완전히 다루어 질 assert(times_two(2) == 4)것이지만 여전히 버그가있는 코드입니다! 메모리 누수 필요 없음 :)
Andres F.

2
좋은 지적이며 버그가없는 응용 프로그램의 가능성에 대한 관에서 더 크고 근본적인 손톱이라는 것을 알고 있지만 이미 여기에 추가되어 있으며 다루지 않은 것을 추가하고 싶었습니다. 기존 답변. 데이터베이스 연결이 더 이상 필요하지 않을 때 연결 풀로 다시 해제되지 않아 응용 프로그램이 중단되었다는 소식을 들었습니다. 메모리 누수는 단지 자원 관리 오류의 전형적인 예입니다. 필자의 요점은 일반적으로 적절한 자원 관리를 완전히 테스트 할 수 없다는 점을 추가하는 것이 었습니다.
Derek W

좋은 지적. 동의했다.
Andres F.

3

프로그램의 모든 경로를 테스트하면 모든 버그를 찾을 수 있습니까?

이미 말했듯이 대답은 아니오입니다.

그렇지 않다면 왜 안됩니까?

말한 것 외에도 다른 수준에서 나타나는 버그가 있으며 단위 테스트로는 테스트 할 수 없습니다. 몇 가지 언급 만하면됩니다.

  • 통합 테스트에서 발견 된 버그 (단위 테스트는 결국 실제 리소스를 사용해서는 안 됨)
  • 요구 사항의 버그
  • 디자인과 아키텍처의 버그

2

모든 경로를 테스트한다는 것은 무엇을 의미합니까?

다른 대답은 훌륭하지만 "프로그램을 통과하는 모든 경로가 테스트됩니다"라는 조건 자체가 모호하다는 점을 덧붙이고 싶습니다.

이 방법을 고려하십시오.

def add(num1, num2)
  foo = "bar"  # useless statement
  $global += 1 # side effect
  num1 + num2  # actual work
end

당신이 주장하는 테스트를 작성한다면 add(1, 2) == 3, 코드 커버리지 도구는 모든 라인이 실행되었음을 알려줍니다. 그러나 실제로 글로벌 부작용이나 쓸모없는 과제에 대해서는 아무것도 주장하지 않았습니다. 그 줄은 실행되었지만 실제로 테스트되지 않았습니다.

돌연변이 테스트는 이와 같은 문제를 찾는 데 도움이됩니다. 돌연변이 테스트 도구에는 코드를 "돌연변이"하고 테스트가 여전히 통과하는지 확인하기 위해 미리 결정된 방법 목록이 있습니다. 예를 들면 다음과 같습니다.

  • 하나의 돌연변이는이 변경 될 수 있습니다 +=-=. 이 돌연변이는 테스트 실패를 유발하지 않으므로 테스트가 전체 부작용에 대해 의미있는 것을 주장하지 않는다는 것을 증명합니다.
  • 다른 돌연변이는 첫 번째 줄을 삭제할 수 있습니다. 이 돌연변이는 테스트 실패를 유발하지 않으므로 테스트에서 과제에 대해 의미있는 것을 주장하지 않는다는 것을 증명합니다.
  • 또 다른 돌연변이는 세 번째 줄을 삭제할 수 있습니다. 테스트 실패가 발생하며,이 경우 테스트에서 해당 라인에 대해 무언가를 주장하는 것으로 나타납니다.

본질적으로 돌연변이 테스트는 테스트테스트 하는 방법 입니다. 그러나 가능한 모든 입력 세트로 실제 기능을 테스트하지 않는 것처럼 가능한 모든 돌연변이를 실행하지 않으므로 다시 제한됩니다.

우리가 할 수있는 모든 테스트는 버그없는 프로그램으로 나아가는 휴리스틱입니다. 완벽한 것은 없습니다.


0

음 ... 네, 실제로 모든 경로가 프로그램을 "통과"하면 테스트됩니다. 그러나 이는 모든 변수를 포함하여 프로그램이 가질 수있는 모든 가능한 상태의 전체 공간을 통한 모든 가능한 경로를 의미합니다. 아주 간단한 정적으로 컴파일 된 프로그램 (예 : 이전 포트란 수 크 런처)도 가능하지는 않지만 적어도 상상할 수는 없습니다. 정수 변수가 두 개인 경우 기본적으로 포인트를 연결하는 모든 가능한 방법을 처리합니다 2 차원 그리드; 실제로 Traveling Salesman처럼 보입니다. 들어 n은 변수, 당신은 상대하고있는 N 차원 공간 때문에 실제 프로그램에 대한 작업이 완전히 untractable입니다.

더 나쁜 : 심각한 물건, 당신은 할 수 없습니다 원시 변수의 단지 고정 된 수 있지만, 함수 호출에 즉시 변수를 만들거나 튜링 완전한 언어로 가능한 한, 가변 크기 변수 ... 나처럼 아무것도. 이로 인해 상태 공간이 무한 차원으로 만들어져 엄청나게 강력한 테스트 장비가 제공되는 경우에도 모든 적용 범위에 대한 모든 희망을 산산조각냅니다.


사실 ... 사실은 그렇게 어둡지 않습니다. 이다proove 올바른로 전체 프로그램을,하지만 당신은 몇 가지 아이디어를 포기해야합니다.

첫째 : 선언적인 언어로 전환하는 것이 좋습니다. 어떤 이유로 든 명령형 언어는 항상 가장 인기가 높았지만 실제 상호 작용과 알고리즘을 혼합하는 방식 은 "올바른" 이라는 의미 를 말하기조차 매우 어렵습니다 .

순전히 기능적인 프로그래밍 언어 에서 훨씬 더 쉽습니다 . 수학 함수 의 흥미로운 흥미로운 속성 과 실제로 말할 수없는 퍼지 된 실제 상호 작용을 명확하게 구분합니다 . 함수의 경우“올바른 동작”을 지정하는 것이 매우 쉽습니다. 가능한 모든 입력 (인수 유형에서)에 해당하는 원하는 결과가 나오면 함수가 올바르게 동작하는 것입니다.

자, 당신은 그것이 여전히 다루기 어려운 것이라고 말합니다. 결국 모든 가능한 논쟁의 공간은 일반적으로 무한 차원입니다. 사실 – 단일 기능의 경우, 순진한 적용 범위 테스트조차 명령형 프로그램에서 기대했던 것보다 훨씬 더 앞선 것입니다! 그러나 게임을 변화시키는 놀라운 강력한 도구가 있습니다 : 보편적 정량 / 매개 변수 다형성 . 기본적으로 이것은 매우 일반적인 종류의 데이터에 함수를 작성할 수있게하며, 간단한 데이터 예제에서 작동하면 가능한 모든 입력에 대해 작동 할 수 있습니다.

적어도 이론적으로는. 이 유형을 완전히 제시 할 수있는 너무 일반적인 유형을 찾는 것은 쉽지 않습니다. 일반적으로 종속 유형 언어 가 필요하며 사용하기가 어려운 경향이 있습니다. 그러나 파라 메트릭 다형성만으로 기능적 스타일로 작성하면 이미 "보안 수준"이 크게 향상됩니다. 모든 버그를 반드시 찾아야하는 것은 아니지만 컴파일러는이를 잘 숨겨야합니다.


첫 문장에 동의하지 않습니다. 프로그램의 모든 상태를가는 것은, 그 자체로 인식하지 않는 어떤 버그를. 충돌 및 명시 적 오류를 확인하더라도 실제 기능은 아직 확인하지 않았으므로 오류 공간의 일부만 다루었습니다.
Matthew 읽기

@MatthewRead :이 결과를 적용하면 "오류 공간"은 모든 상태 공간의 적절한 하위 공간입니다. 물론 "올바른"상태조차도 철저한 테스트를하기에는 너무 큰 공간을 차지하기 때문에 가상의 것입니다.
leftaroundabout
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.