가이드라인: 부울 및 경계에 대한 테스트 아이디어
테스트 아이디어는 그럴듯한 소프트웨어 결함과 해당 결함을 가장 잘 발견할 수 있는 방법을 기반으로 합니다. 이 가이드라인은 부울 표현식과 경계 조건에 대한 테스트 아이디어를 개발하는 방법을 설명합니다.
관계
기본 설명

소개

테스트 아이디어는 결함 모델을 기반으로 합니다. 결함 모델이란 소프트웨어에서 그럴듯한 결함을 정의하여 해당 결함을 가장 잘 드러낼 수 있는 방법을 찾는 개념입니다. 이 가이드라인은 부울 및 관계식에서 테스트 아이디어를 작성하는 방법을 보여 줍니다. 먼저 코드를 보고 기법에 동기 부여하며, 코드가 아직 작성되지 않았거나 사용 불가능한 경우 적용하는 방법을 설명합니다.

부울 표현식

폭발물 폭파를 관리하기 위한 (가상의) 시스템에서 다음 코드 스니펫을 가정해 보십시오. 안전 시스템의 파트이며 "폭발물 폭파" 단추가 올바르게 눌리도록 제어합니다.

if (publicIsClear || technicianClear) {
    bomb.detonate();
}

코드가 잘못되었습니다. ||&&여야 합니다. 이러한 실수는 좋지 못한 영향을 미칩니다. 폭발물 기술자와 일반인 양쪽 모두가 안전할 때 폭발물을 폭파하는 대신 둘 중 하나가 안전할 때 시스템이 폭발물을 폭파합니다.

어떤 테스트로 이러한 버그를 발견합니까?

기술자와 일반인 양쪽 모두가 안전할 때 단추가 눌리는 테스트를 고려합니다. 코드는 폭발물이 폭파되도록 합니다. 그러나 중요한 것은 올바른 코드(&& 사용)에서도 같은 결과가 발생한다는 것입니다. 그러므로 이 테스트는 이러한 결함 발견 시 유용하지 않습니다.

비슷한 경우로 기술자와 일반인이 폭발물 옆에 있을 때 이 잘못된 코드는 올바르게 동작하여 폭발물이 폭파되지 않게 합니다.

버그를 찾으려면 작성된 코드에서는 올바른 코드와는 다르게 평가하는 경우가 있어야 합니다. 예를 들어, 일반인은 안전하지만 폭발물 기술자는 계속 폭발물 옆에 있습니다. 다음은 표 양식의 모든 테스트입니다.

publicIsClear

technicianClear

작성된 코드...

가능한 코드...

 

true

true

폭파

폭파됨

테스트가 유용하지 않음(해당 결함에 대해)

true

false

폭파

폭파되지 않음

유용한 테스트

false

true

폭파

폭파되지 않음

유용한 테스트

false

false

폭파되지 않음

폭파되지 않음

테스트가 유용하지 않음(해당 결함에 대해)


이러한 특정 결함을 찾는 데는 중간에 있는 두 개의 테스트가 유용합니다. 그러나 둘 중 하나만으로도 결함을 찾을 수 있으므로 중복이며, 둘 다 실행할 필요는 없습니다.

다른 경우로 표현식이 잘못되는 경우가 있습니다. 다음은 부울 표현식에서의 일반적인 실수에 대한 두 가지 목록입니다. 왼쪽의 결함은 여기서 논의되는 기법으로 모두 발견됩니다. 오른쪽의 결함은 그렇지 않습니다. 즉 이 기법으로 모든 결함을 발견할 수 있는 것은 아니지만 여전히 유용합니다.

발견된 결함

발견되지 않을 수 있는 결함

잘못된 연산자가 사용됨: a || b는 a && b여야 함 잘못된 변수가 사용됨: a&&b&&c는 a&& x&&d여야 함
부정이 생략되거나 올바르지 않음: a||b는 !a||b가 되거나 ! a||b는 a||b여야 함 표현식이 너무 간단함: a&&b는 a&&b&&c여야 함
괄호 안의 표현식이 잘못 구성됨: a&&b||c는 a&&(b||c) 왼쪽 열에 하나 이상의 결함이 있는 표현식
표현식이 전체적으로 복잡함: a&&b&&ca&&b여야 함
(이러한 결함은 자주 발생하지는 않지만 다른 이유에 유용한 테스트로 쉽게 찾을 수 있음)
 

이러한 아이디어는 어떻게 사용됩니까? a&&!b. 다음과 같이 진리표를 구성할 수 있습니다.

a

b여야 함

a&&!b
(작성된 코드)

가능한 코드:
a||!b

가능한 코드:
! a&&!b

가능한 코드:
a&&b

...

true

true

false

true

false

true

...

true

false

true

true

false

false

...

false

true

false

false

false

false

...

false

false

false

true

true

false

...


모든 가능성을 확인해 본 결과 첫 번째, 두 번째 및 네 번째 가능성이 필요하다는 것을 알 수 있습니다. 세 번째 표현식에는 다른 표현식으로 찾을 수 없는 결함이 없으므로 시도할 필요가 없습니다. (표현식이 더 복잡해질수록 불필요한 사례에 따른 절약이 더 많아집니다.)

물론 보통은 이런 표를 만들지 않습니다. 운좋게도 그럴 필요도 없습니다. 단순한 표현식에 필요한 사례는 기억하기 쉽습니다. (다음 섹션을 참조하십시오.) A&&B||C와 같이 복잡한 표현식의 경우 AND 및 OR 조합에 대한 테스트 아이디어에 나열되어 있는 두 개나 세 개 이상의 연산자를 가진 테스트 아이디어를 참조하십시오. 더 복잡한 표현식의 경우 프로그램을 사용하여 테스트 아이디어를 생성할 수 있습니다.

단순한 부울 표현식에 대한 표

표현식이 A&&B이면 다음으로 테스트하십시오.

A

B

true

true

true

false

false

true


표현식이 A||B이면 다음으로 테스트하십시오.

A

B

true

false

false

true

false

false


표현식이 A1 && A2 && ... && An이면 다음으로 테스트하십시오.

A1, A2, ..., and An은 모두 true

A1은 false이고 나머지는 모두 true

A2는 false이고 나머지는 모두 true

...

An은 false이고 나머지는 모두 true


표현식이 A1 || A2 || ... || An이면 다음으로 테스트하십시오.

A1, A2, ..., and An은 모두 false

A1은 true이고 나머지는 모두 false

A2는 true이고 나머지는 모두 false

...

An은 true이고 나머지는 모두 false


표현식이 A 이면 다음으로 테스트하십시오.

A

true

false


따라서 a&&!b 위의 첫 번째 테이블을 적용하되, b를 역으로 하여(부정 표시되어 있으므로) 이 테스트 아이디어 목록을 가져오십시오.

  • A true, B false
  • A true, B true
  • A false, B false

관계식

결함이 있는 코드의 다른 예제는 다음과 같습니다.

if (finished < required) {
    siren.sound();
}

<<=. 이러한 실수는 자주 발생합니다. 부울 표현식으로 테스트 값 표를 생성하여 결함을 발견하는 방법을 찾을 수 있습니다.

finished

required

작성된 코드...

가능한 코드...

1

5

사이렌 소리가 남

사이렌 소리가 남

5

5

사이렌 소리가 안남

사이렌 소리가 남

5

1

사이렌 소리가 안남

사이렌 소리가 안남


보다 일반적으로 finished=required이면 언제든 결함을 발견할 수 있습니다. 그럴듯한 결함의 분석에서 테스트 아이디어에 대해 다음 규칙을 얻을 수 있습니다.

표현식이 A<B 또는 A>=B이면 다음으로 테스트하십시오.

A=B

A가 B보다 약간 작음


표현식이 A>B 또는 A<=B이면 다음으로 테스트하십시오.

A=B

A가 B보다 약간 큼


"약간"의 의미는 무엇입니까? A와 B가 정수이면 A는 B보다 작거나 커야 합니다. 부동 소수점 숫자이면 A는 B에 아주 가까운 숫자여야 합니다. (B에 가장 근접한 부동 소수점 숫자일 필요는 없습니다.)

결합된 부울 및 관계식에 대한 규칙

이 예제와 같이 대부분의 관계 연산자는 부울 표현식 내에서 볼 수 있습니다.

if (finished < required) {
    siren.sound();
}

관계식의 규칙에서 다음과 같은 테스트 아이디어가 생깁니다.

  1. finished 는 required와 같음
  2. finished 는 표현식: required

부울 표현식의 규칙에서 다음과 같은 테스트 아이디어가 생깁니다.

  1. finished < required는 true가 되어야 함
  2. finished < required는 false가 되어야 함

표현식: finished 는 표현식: required , finished < required 가 true인 경우 문자를 기록할 필요가 없습니다.

이 표현식이 false인 경우에는 작성할 필요가 없습니다. finished = required , , finished < required .

관계식에 부울 연산자(&&||)가 없으면 부울 표현식이라는 사실을 무시하십시오.

다음과 같이 더 복잡한 부울 및 관계 연산자 조합이 있습니다.

if (count<5 || always) {
   siren.sound();
}

관계식에서 다음을 얻을 수 있습니다.

  • count가 5보다 약간 작음
  • count가 5와 같음

부울 표현식에서 다음을 얻을 수 있습니다.

  • count<5 true, always false
  • count<5 false, always true
  • count<5 false, always false

이는 세 개의 특정한 테스트 아이디어로 결합될 수 있습니다. (여기에서 count는 정수임에 유의하십시오.)

  1. count=4, always false
  2. count=5, always true
  3. count=5, always false

count=5가 두 번 사용되었음에 유의하십시오. 다른 값을 사용할 수 있도록 허용하려면 한 번만 사용하는 것이 좋습니다. count 5를 두 번 사용하여 이 표현식을 테스트할 이유가 없습니다. 5를 한 번만 사용하고 다음에는 다른 값을 사용하여 false 결과를 얻는 것은 어떨까요(예: count<5)? 가능은 하지만 위험한 시도입니다. 실수를 저지르기 쉽기 때문입니다. 다음을 시도했다고 가정해 보십시오.

  1. count=4, always false
  2. count=5, always true
  3. count<5 false, always false

다음 값을 사용해야만 발견할 수 있는 결함이 있다고 가정해 보십시오: count=5. 이것은 올바른 코드인 경우 true를 생성할 5 값이 count<5 표현식에서는 "false"를 생성함을 의미합니다. 그러나 false 값은 즉시 always 값(즉 true 값)과 or됩니다. 이것은 하위 관계식의 값이 잘못되었어도 전체 관계식의 값은 올바르게 됨을 의미합니다. 결함이 발견되지 않습니다.

보다 특정화되지 않은 다른 count=5인 경우 결함은 발견될 수 있습니다.

관계식이 부울 연산자 오른쪽에 있는 경우 비슷한 문제가 발생합니다.

어떤 하위 표현식이 정확하고 어떤 하위 표현식이 일반적인지 알기 힘들기 때문에 모두 정확하게 작성하는 것이 가장 좋습니다. 대안은 위에서 언급한 부울 표현식 프로그램을 사용하는 것입니다. 이 프로그램은 부울 표현식과 관계식이 혼합된 임의의 표현식에 대한 올바른 테스트 아이디어를 생성합니다.

코드가 없는 테스트 아이디어

개념: 테스트 우선 디자인에서 설명된 것처럼 코드를 구현하기 전에 테스트를 디자인하는 것이 좋습니다. 즉, 코드 예제가 기법의 동기가 되긴 하지만 보통 코드가 없이 적용됩니다. 어떻게 적용될까요?

상태 차트 및 시퀀스 다이어그램과 같은 특정 디자인 아티팩트는 가드(guard)로 부울 표현식을 사용합니다. 이러한 경우는 직접적이며, 부울 표현식에서 아티팩트의 테스트 아이디어 체크리스트로 테스트 아이디어를 간단히 추가합니다. 중간 산출물 가이드라인: 상태 차트 및 활동 다이어그램에 대한 테스트 아이디어를 참조하십시오.

더 복잡한 경우는 부울 표현식이 명시적이 아니라 내재적인 경우입니다. 보통 API 설명의 경우가 이에 해당합니다. 예제는 다음과 같습니다. 아래 메소드를 고려하십시오.

List matchList(Directory d1, Directory d1,
       FilenameFilter excluder);

이러한 메소드 동작의 설명은 다음과 같습니다.

두 디렉토리에 나타나는 모든 파일의 절대 경로 목록을 리턴합니다. 서브디렉토리는 아래 위치합니다. excluder와 일치하는 [...] 파일 이름은 리턴된 목록에서 제외됩니다. excluder는 최상위 레벨에만 적용되고 서브디렉토리의 파일 이름에는 적용되지 않습니다.

"and", "or" 단어는 보이지 않습니다. 그러나 언제 파일 이름이 리턴 목록에 포함됩니까? 첫 번째 디렉토리 and 두 번째 디렉토리에 있고 and 하위 레벨 디렉토리에 있거나 or 특별히 제외되지 않는 경우입니다. 코드는 다음과 같습니다.

if (appearsInFirst && appearsInSecond &&
    (inLowerLevel || !excluded)) {
  add to list
}

표 양식으로 표시된 해당 표현식에 대한 테스트 아이디어는 다음과 같습니다.

appearsInFirst

appearsInSecond

inLower

excluded

true

true

false

true

true

true

false

false

true

true

true

true

true

false

false

false

false

true

false

false


텍스트에서 내재적인 부울 표현식을 발견하는 일반적인 접근 방식은 먼저 설명된 조치(예: "일치하는 이름 리턴")를 나열하는 것입니다. 그런 다음 조치가 수행되는 경우를 설명하는 부울 표현식을 작성하십시오. 모든 표현식에서 테스트 아이디어를 도출하십시오.

해당 프로세스에는 불일치의 여지가 있습니다. 예를 들어, 한 사람이 위에서 사용된 부울 표현식을 적었습니다. 다른 사람은 프로그램이 일치하는 이름을 발견하는 것과 이를 필터링하는 별개의 두 조치가 있다고 말할 수 있습니다. 그러므로 하나의 표현식 대신 다음과 같이 두 가지가 있습니다.

일치 발견:
파일이 첫 번째 디렉토리에 있고 and 두 번째 디렉토리에 같은 이름의 파일이 있을 때 발생합니다.
일치 필터링:
일치하는 파일이 최상위 레벨에 있고 and 이름이 excluder와 일치할 때 발생합니다.

이렇게 다른 접근 방식은 다른 테스트 아이디어로 발전하여 결국 다른 테스트가 됩니다. 그러나 차이점은 특별하게 중요하지는 않습니다. 즉, 올바른 표현식을 찾고 대안을 시도해보는 것이 다른 기법과 더 많은 테스트를 생성하는 것보다 좋습니다. 어떠한 종류의 차이점이 있는지에 관심이 있으면 아래를 참조하십시오.

두 번째 사람은 두 세트의 테스트 아이디어를 얻게 됩니다.

일치를 발견하는 테스트 아이디어:

  • 첫 번째 디렉토리의 파일, 두 번째 디렉토리의 파일(true, true)
  • 첫 번째 디렉토리의 파일, 두 번째 디렉토리에 없는 파일(true, false)
  • 첫 번째 디렉토리에 없는 파일, 두 번째 디렉토리의 파일(false, true)

일치를 발견했을 때 필터링하는 테스트 아이디어:

  • 일치하는 파일이 최상위 레벨에 있음, 이름이 excluder와 일치(true, true)
  • 일치하는 파일이 최상위 레벨에 있음, 이름이 excluder와 일치하지 않음(true, false)
  • 일치하는 파일이 일부 하위 레벨에 있음, 이름이 excluder와 일치하음(false, true)

두 세트의 테스트 아이디어가 결합되었다고 가정해 보십시오. 두 번째 세트의 내용은 파일이 양쪽 디렉토리에 있는 경우만 관련되므로 이는 첫 번째 세트의 첫 번째 아이디어로만 결합될 수 있습니다. 이를 통해 다음을 얻을 수 있습니다.

첫 번째 디렉토리의 파일

두 번째 디렉토리의 파일

최상위 레벨

excluder 일치

true

true

true

true

true

true

true

false

true

true

false

true


일치를 발견하는 두 개의 테스트 아이디어는 해당 표에 표시되지 않습니다. 이는 다음과 같이 추가할 수 있습니다.

첫 번째 디렉토리의 파일

두 번째 디렉토리의 파일

최상위 레벨

excluder 일치

true

true

true

true

true

true

true

false

true

true

false

true

true

false

-

-

false

true

-

-


공백 셀은 열이 무의미함을 표시합니다.

이제 이 표는 첫 번째 사람의 표와 비슷해졌습니다. 같은 용어를 사용하여 유사성을 강조할 수 있습니다. 첫 번째 사람의 표에는 "inLower"라는 이름의 열이 있고 두 번째 사람의 표에는 "최상위 레벨"이라는 열이 있습니다. 값의 의미를 뒤바꿔 이를 변환할 수 있습니다. 이렇게 하면 두 번째 표의 다음과 같은 버전을 얻을 수 있습니다.

appearsInFirst

appearsInSecond

inLower

excluded

true

true

false

true

true

true

false

false

true

true

true

true

true

false

-

-

false

true

-

-


처음 세 행은 첫 번째 사람의 표와 동일합니다. 마지막 두 행은 첫 번째에서 지정되었던 값이 지정되지 않았다는 점만 다릅니다. 이를 통해 코드가 작성된 방법에 대한 가정에 이릅니다. 첫 번째는 복잡한 부울 표현식을 가정합니다.

if (appearsInFirst && appearsInSecond &&
    (inLowerLevel || !excluded)) {
  add to list
}

두 번째는 중첩된 부울 표현식을 가정합니다.

if (appearsInFirst && appearsInSecond) {
    // found match.
    if (inTopLevel && excluded) {
// filter it
    }
}     

둘 사이의 차이점은 첫 번째의 테스트 아이디어에서는 결함이 적용되지 않아서 두 번째의 테스트 아이디어에서 발견하지 못한 두 개의 결함을 발견했다는 것입니다.

  1. 첫 번째 구현에서는 괄호에 결함이 있을 수 있습니다. || 주위의 괄호가 맞습니까, 틀립니까? 두 번째 구현에는 괄호와 ||이 없어 결함이 있을 수 없습니다.
  2. 첫 번째 구현의 테스트 요구사항은 두 번째 표현식 &&||여야 하는지 확인합니다. 두 번째 구현에서 명시적인 &&는 내재적인 &&로 바뀝니다. 원래 ||-for-&& 결함은 없습니다. (중첩이 잘못될 수 있지만 이 기법에서는 이를 다루지 않습니다.)