프로그래밍웹 교과서
이 페이지의 목차
3단원 · 7차시

단위 테스트와 통합 테스트

단위 테스트통합 테스트테스트 사례경계값

코드 예제: Python · C · 예상 50분

학습 목표

  • 단위 테스트와 통합 테스트의 목적과 차이를 설명한다.
  • 함수별 테스트 사례를 만들어 실행한다.

관련 성취기준

  • 프그03-04

    구현된 프로그램을 대상으로 단위 테스트, 통합 테스트, 시스템 테스트를 할 수 있다.

생각 열기 — “돌아간다”와 “맞다”

6차시에서 합친 프로그램을 실행해 봤다. 오류 없이 결과가 나왔다.

인원 5명 (제외 2건)
평균 83.8  최고 100  최저 64

모둠에서 “됐다”고 했다. 그런데 다음 날 선생님이 점수 0을 넣어 보니 인원에서 빠져 있었다. is_valid 에 0 < 점수 라고 적혀 있었던 것이다.

테스트는 대조하는 일이다

1단원 4차시에서 테스트를 이렇게 정의했다 — 미리 정한 입력을 넣고 미리 정한 결과가 나오는지 대조하는 일. “실행해 보니 되네”가 테스트가 아닌 이유가 생각 열기에 그대로 나온다. 넣어 본 값이 전부 정상값이었기 때문에 경계에 있는 잘못이 드러나지 않았다.

그래서 테스트에는 테스트 사례를 미리 만들어 둔다.

테스트 사례에 반드시 있어야 할 네 칸
칸무엇을 적는가예
이름무엇을 확인하는 사례인가경계값 0
넣을 값입력is_valid(0)
기대 결과손으로 미리 정한 결과참
판정실제 결과와 같은가통과 / 실패

기대 결과는 프로그램을 돌려 얻지 않는다. 2차시 명세서의 「확인 방법」 표와 4차시 책상 검사에서 손으로 계산해 둔 값이 여기로 온다. 이 세 문서가 이어지는 지점이다.

단위 테스트 — 부품 하나씩

단위 테스트(unit test) — 부품 하나만 따로 떼어 정한 입력에 정한 결과가 나오는지 확인하는 시험는 부품 하나를 다른 부품과 떼어 놓고 확인한다. is_valid 를 시험할 때는 summarize 나 화면 출력이 끼어들지 않는다.

떼어 놓는 이유는 잘못의 위치를 좁히기 위해서다. is_valid 의 단위 테스트가 통과했다면, 나중에 통계가 틀렸을 때 그 부품은 의심 목록에서 빼도 된다.

어떤 값을 넣을 것인가

1단원 5차시에서 다룬 네 갈래를 그대로 쓴다. 생각 열기의 잘못은 경계값을 안 넣었기 때문에 발견되지 않았다.

is_valid(0 이상 100 이하)의 테스트 사례
갈래넣을 값기대 결과이 값이 잡아내는 잘못
정상값75참기본 동작이 아예 안 되는 경우
경계값0참0 < 점수 처럼 등호를 빠뜨린 경우
경계값100참점수 < 100 처럼 등호를 빠뜨린 경우
경계 바로 밖−1거짓아래쪽 범위를 잘못 잡은 경우
경계 바로 밖101거짓위쪽 범위를 잘못 잡은 경우

테스트를 코드로 만든다

테스트 사례를 손으로 하나씩 실행하면 시간이 걸리고, 코드를 고칠 때마다 다시 해야 한다. 그래서 테스트도 코드로 만든다.

Pythonis_valid 의 단위 테스트
def is_valid(score):
    return 0 <= score <= 100

cases = [
    ("정상값", 75, True),
    ("경계값 0", 0, True),
    ("경계값 100", 100, True),
    ("경계 밖 -1", -1, False),
    ("경계 밖 101", 101, False),
]

passed = 0
for name, given, expected in cases:
    actual = is_valid(given)
    ok = actual == expected
    print(f"[{'통과' if ok else '실패'}] {name}: is_valid({given}) -> {actual}")
    if ok:
        passed += 1

print(f"{passed}/{len(cases)} 통과")
확인된 실행 결과
[통과] 정상값: is_valid(75) -> True
[통과] 경계값 0: is_valid(0) -> True
[통과] 경계값 100: is_valid(100) -> True
[통과] 경계 밖 -1: is_valid(-1) -> False
[통과] 경계 밖 101: is_valid(101) -> False
5/5 통과
Cis_valid 의 단위 테스트확인된 출력
#include <stdio.h>

int is_valid(int score)
{
    return score >= 0 && score <= 100;
}

int main(void)
{
    const char *names[] = {"정상값", "경계값 0", "경계값 100", "경계 밖 -1", "경계 밖 101"};
    int given[]    = {75, 0, 100, -1, 101};
    int expected[] = { 1, 1,   1,  0,   0};
    int passed = 0;
    int total = 5;

    for (int i = 0; i < total; i++) {
        int actual = is_valid(given[i]);
        int ok = (actual == expected[i]);
        printf("[%s] %s: is_valid(%d) -> %d\n",
               ok ? "통과" : "실패", names[i], given[i], actual);
        if (ok) passed++;
    }
    printf("%d/%d 통과\n", passed, total);
    return 0;
}
실행 결과
[통과] 정상값: is_valid(75) -> 1
[통과] 경계값 0: is_valid(0) -> 1
[통과] 경계값 100: is_valid(100) -> 1
[통과] 경계 밖 -1: is_valid(-1) -> 0
[통과] 경계 밖 101: is_valid(101) -> 0
5/5 통과

테스트 사례가 목록으로 적혀 있다는 점이 중요하다. 사례를 하나 더 넣고 싶으면 목록에 한 줄만 더하면 된다. 그리고 코드를 고칠 때마다 이것을 한 번 돌리면 되던 것이 깨졌는지 바로 알 수 있다 — 회귀 테스트가 명령 한 번이 된다.

통합 테스트 — 이어 붙인 뒤

통합 테스트(integration test) — 부품을 이어 붙인 상태에서 주고받는 값이 맞는지 확인하는 시험는 부품 사이를 본다. 6차시에서 다룬 어긋남 — 자료형, 값의 순서와 이름, 값의 뜻 — 이 여기서 잡힌다.

단위 테스트를 통과한 부품들로 통합 테스트를 하면, 문제가 났을 때 원인이 “부품 사이”로 좁혀진다. 이것이 두 단계로 나누는 이유다.

같은 프로그램을 두 단계로 확인한다
단위 테스트통합 테스트
보는 범위부품 하나이어 붙인 부분
넣는 것그 부품이 받는 값프로그램 전체의 입력
잡히는 잘못계산식·조건이 틀린 경우자료형·순서·뜻이 어긋난 경우
예is_valid(0) 이 거짓을 돌려준다summarize 가 문자열을 받아 계산하려 한다
통과 못 하면그 부품 안을 본다두 부품이 주고받는 값을 본다

우리 프로그램의 통합 테스트 사례는 2차시 명세서의 「확인 방법」 표가 그대로 된다.

이름 넣을 값 기대 결과
정상 + 범위 밖 섞임 88, 92, 75, 100, 64, −5, 120 인원 5명 (제외 2건) / 평균 83.8 최고 100 최저 64
경계값만 0, 100 인원 2명 (제외 0건) / 평균 50.0 최고 100 최저 0
값이 하나뿐 77 인원 1명 (제외 0건) / 평균 77.0 최고 77 최저 77
전부 범위 밖 −1, 200 쓸 수 있는 점수가 없다

「경계값만」 사례가 생각 열기의 잘못을 잡는다. 0 < 점수 로 되어 있으면 인원이 1명으로 나와 기대와 어긋난다.

활동

실습

정상 입력·경계값·잘못된 입력으로 테스트 사례 만들기

약 45분

6차시에서 합친 프로그램을 대상으로 테스트를 만든다.

1단계 — 단위 테스트 사례를 만든다 (15분)

부품을 하나 골라 사례를 최소 5개 만든다. 네 갈래가 모두 들어가야 한다.

이름 갈래 넣을 값 기대 결과 (손으로) 실제 결과 판정
정상값
경계값
경계값
경계 바로 밖
경계 바로 밖

경계값은 반드시 짝으로 넣는다 — 0이면 0과 −1, 100이면 100과 101.

2단계 — 테스트를 코드로 만든다 (15분)

위 표를 본문 예제처럼 목록으로 만들어 한 번에 돌리는 코드로 옮긴다. 사례를 나중에 더할 수 있게 목록 형태를 지킨다.

3단계 — 일부러 망가뜨려 본다 (5분)

부품의 조건을 일부러 한 글자 고친다. 예를 들어 >= 를 > 로 바꾼다.

  • 테스트가 실패를 잡아내는가?
  • 잡아내지 못했다면 그 경계값 사례가 빠진 것이다. 사례를 추가한다.

고친 글자는 반드시 되돌려 놓는다.

4단계 — 통합 테스트 (10분)

2차시 명세서의 「확인 방법」 표를 그대로 옮겨 프로그램 전체를 확인한다.

넣을 값 기대 결과 실제 결과 판정

실패한 사례가 있으면 어느 부품 사이에서 어긋났는지 적는다. 고치는 것은 다음 차시에서 한다.

개념 확인 문제

보기를 누르면 바로 채점된다. 틀려도 해설을 읽으면 된다.

프로그램을 실행해 오류 없이 결과가 나왔다. 이것을 테스트라고 할 수 없는 이유는?프그03-04

0 이상 100 이하를 확인하는 부품을 시험할 때, 경계값을 0과 −1처럼 짝으로 넣는 이유는?프그03-04

단위 테스트와 통합 테스트를 두 단계로 나누는 가장 큰 이유는?프그03-04

테스트 사례를 목록으로 만들어 한 번에 돌리는 코드로 작성했을 때 얻는 이점은?프그03-04

[OX] 만들어 둔 테스트를 모두 통과하면 그 프로그램에 잘못이 없다고 말할 수 있다.프그03-04

테스트 사례의 '기대 결과'를 정하는 올바른 방법은?프그03-04

더 알아보기

심화테스트 사례를 고르는 요령 — 값을 묶어서 본다

0 이상 100 이하를 확인할 때 0부터 100까지 101개를 전부 넣어 볼 필요는 없다. 같은 결과가 나오는 값들을 한 덩어리로 보고 덩어리마다 하나씩만 고르면 된다. 여기서는 「0보다 작은 값」·「0에서 100 사이」·「100보다 큰 값」 세 덩어리다. 각 덩어리에서 하나씩, 그리고 덩어리의 경계에서 값을 고른다. 이렇게 하면 적은 사례로 넓게 확인할 수 있다. 실무에서 테스트 사례를 설계하는 기본 방법이다.

실무테스트를 먼저 쓰는 순서

1단원 5차시에서 잠깐 다룬 방법이다. 부품을 만들기 전에 테스트를 먼저 적는다. is_valid 를 만들기 전에 “0을 넣으면 참, −1을 넣으면 거짓”을 먼저 적어 두는 것이다. 이렇게 하면 두 가지가 좋아진다. 첫째, 테스트를 적으려면 입력과 출력을 정해야 하므로 무엇을 만들지가 분명해진다. 둘째, 만들면서 바로바로 확인된다. 이 단원에서는 설계 → 구현 → 테스트 순서로 배웠지만, 익숙해지면 순서를 바꿔 보는 것도 좋다.

심화자동으로 돌아가는 테스트

본문의 테스트 코드는 결과를 화면에 찍고 사람이 읽는다. 실무에서는 여기서 한 걸음 더 나아가 테스트가 실패하면 프로그램이 실패로 끝나게 만든다. 그러면 코드를 올릴 때마다 자동으로 테스트가 돌고, 하나라도 실패하면 올라가지 않게 막을 수 있다. 이 웹 교과서도 같은 방식으로 만들어졌다 — 본문의 예제 코드를 실제로 실행해 적어 둔 기대 출력과 대조하고, 하나라도 어긋나면 배포가 멈춘다. 지어낸 출력을 실을 수 없게 기계가 막는 것이다.

이어지는 학습

이 차시를 이해하려면 다음을 먼저 알아야 한다.

  • 6차시에서 통합한 프로그램 — 프로그래밍 프그03-03

교과서에서 찾기

교과서해당 위치쪽수
성림출판(Python)프로그램 설계와 구현 › 01 프로그램 개발 과정 > 4 프로그램 테스트219쪽
씨마스커뮤니케이션(Python)프로그램 설계와 구현 › 2 프로그래밍 실제222쪽
삼양미디어(Python)프로그램의 설계와 구현 › 02 프로그램 개발 프로젝트190쪽
삼양미디어(C)프로그램의 설계와 구현 › 02 프로그램 개발 프로젝트197쪽
씨마스(C)프로그래밍 설계와 구현 › 3 프로그래밍의 실제 Ⅱ236쪽
웅보출판사(C)프로그램 설계와 구현 › Chapter 04 프로그램 테스트227쪽