사전 중간 테스트
개념

중간 테스트

gabury1고친 사람 github-actions[bot]

중간 테스트는 머신 한 대 안에서만 도는 테스트입니다. 프로그램 하나 안에서 도는 작은 테스트와 여러 머신에 걸치는 큰 테스트 사이에 놓입니다. 머신 안에서는 진짜 데이터베이스나 서버를 붙여 놓고 확인해도 됩니다. 작은 테스트보다 느린 대신 프로그램끼리 이어 봐야 드러나는 잘못을 잡습니다.

쉽고 빠른 이해

무슨 일을 하나 — 테스트가 쓸 수 있는 것을 머신(컴퓨터) 한 대 안으로 묶어 둡니다. 주문 저장 코드를 확인할 때 가짜 저장소 대신 같은 머신에 띄운 진짜 데이터베이스에 붙여 돌리는 식입니다.

왜 이렇게 하나 — 가짜는 만든 사람이 짐작한 대로만 움직입니다. 쿼리가 진짜 데이터베이스에서도 통하는지는 붙여 봐야 압니다. 그래도 다른 머신까지 가면 너무 느리고 잘 흔들립니다. 그래서 한 대에서 멈춥니다.

어떻게 도나

  1. 확인할 서버와 데이터베이스를 테스트와 같은 머신에 띄웁니다
  2. 테스트가 그 머신 안에서 요청을 보냅니다
  3. 다른 머신으로 나가는 요청은 허락하지 않습니다

대가 — 프로그램을 띄우고 디스크를 쓰는 데 시간이 들어 작은 테스트보다 느립니다. 운영체제나 다른 프로그램 사정에 따라 결과가 가끔 흔들립니다.

언제 고르나 — 작은 테스트로 확인할 수 있으면 그쪽을 씁니다. 진짜 데이터베이스나 서버를 붙여야만 보이는 잘못이 있을 때 중간 테스트를 고릅니다.

상세

이 절은 테스트를 크기로 나누는 잣대부터 봅니다. 그다음 세 칸 중 가운데 칸이 무엇을 허락하고 무엇을 막는지 봅니다. 주문 서버와 데이터베이스를 한 머신에 띄운 테스트 한 벌을 가지고 중간 테스트가 얻는 것과 치르는 것을 보입니다.

밴드 연습을 떠올려 봅시다. 혼자 집에서 연습하면 언제 해도 금방 끝나고 조건이 늘 같습니다. 연습실에 모여 합주하면 서로 박자가 맞는지 들을 수 있습니다. 대신 모이고 장비를 꽂는 데 시간이 듭니다. 중간 테스트는 연습실 합주입니다.

테스트 크기라는 잣대

테스트 크기는 테스트가 어디서 도느냐로 테스트를 나눕니다. 코드 줄 수와는 상관이 없습니다. 이 잣대를 따로 두는 까닭은 테스트가 얼마나 빨리 끝나는지, 돌릴 때마다 같은 결과를 내는지가 도는 곳에 달려 있기 때문입니다.

잣대는 세 칸입니다. 이 세 칸은 구글이 자기 테스트를 나누려고 세운 틀입니다.

프로세스는 실행 중인 프로그램 하나입니다. 아래 표는 칸마다 테스트가 도는 곳을 모았습니다.

크기 도는 곳
작은 테스트 프로세스 하나 안
중간 테스트 머신 한 대 안
큰 테스트 여러 머신에 걸쳐도 됨

중간 테스트는 가운데 칸입니다. 작은 테스트에 걸린 제한을 대부분 풀고 하나만 남깁니다. 남는 제한이 「머신 한 대」입니다.

중간 테스트가 허락하는 것

작은 테스트가 막던 것이 이 칸에서 풀립니다. 이 소절은 풀리는 것을 하나씩 짚고 나서 표로 모읍니다.

첫째, 프로세스를 여럿 띄울 수 있습니다. 테스트 프로세스 옆에 데이터베이스 프로세스와 서버 프로세스를 따로 띄워 붙여도 됩니다.

둘째, 스레드를 여럿 써도 됩니다. 스레드는 한 프로세스 안에서 따로 도는 실행 흐름입니다. 서버 코드가 요청을 여러 흐름으로 나눠 받는다면 그대로 확인할 수 있습니다.

작은 테스트는 언어에 따라 스레드를 하나만 쓰게 막기도 합니다. 여러 흐름이 도는 순서는 매번 달라질 수 있어서 결과가 흔들리기 때문입니다.

셋째, 멈춰 기다리는 호출을 해도 됩니다. 이런 호출을 블로킹 호출이라고 부릅니다. 답이 올 때까지 프로그램이 그 줄에서 멈추기 때문입니다.

블로킹 호출의 대표가 입출력입니다. 입출력은 프로그램이 디스크나 네트워크처럼 바깥과 데이터를 주고받는 일입니다. 중간 테스트는 디스크에 파일을 쓰고 읽을 수 있습니다.

네트워크는 localhost 까지만 됩니다. localhost 는 자기 머신을 가리키는 이름입니다. localhost 로 보낸 요청은 네트워크 장비를 거치지 않고 같은 머신 안의 다른 프로세스에 닿습니다.

할 수 있는 것 작은 테스트 중간 테스트
프로세스 여럿 띄우기 ✗ ✓
스레드 여럿 쓰기 언어에 따라 막음 ✓
디스크 읽기와 쓰기 ✗ ✓
localhost 로 네트워크 호출 ✗ ✓
다른 머신으로 네트워크 호출 ✗ ✗

머신 밖을 막는 까닭

남은 제한 하나가 다른 머신으로 가는 네트워크입니다. 테스트를 느리게 하고 결과를 흔드는 원천 가운데 네트워크가 가장 큽니다. 그래서 다른 제한을 다 풀어도 이것 하나는 남겨 둡니다.

머신 한 대 안이면 테스트가 필요한 것을 전부 직접 띄우고 직접 끕니다. 다른 머신이 바쁜지, 그 사이 네트워크가 막혔는지 같은 사정을 만나지 않습니다. 결과는 테스트가 직접 띄우고 끄는 것에만 달려 있게 됩니다.

한 머신에 담긴 한 벌

이 소절은 중간 테스트 한 벌이 무엇을 머신 안에 두는지 그림으로 봅니다. 테스트가 주문 서버를 부르고, 주문 서버는 데이터베이스에 주문을 적습니다. 셋이 각각 다른 프로세스지만 모두 한 머신 위에 있습니다.

flowchart TD
    subgraph M["머신 한 대"]
        T["테스트"] -->|"localhost 요청"| O["주문 서버"]
        O -->|"localhost 요청"| D["데이터베이스"]
    end
    O -.->|"막힘"| X["다른 머신의 서버"]

실선 둘은 머신 안에서 오가는 요청이라 허락됩니다. 점선은 머신 경계를 넘는 요청이라 막힙니다. 테스트 코드뿐 아니라 테스트가 띄운 서버도 머신 밖으로는 요청을 못 보냅니다. 이 점선이 허락되면 큰 테스트가 됩니다.

테스트 코드에서는 크기가 연결 주소에 드러납니다. 아래 코드는 주문 저장소를 같은 머신에 띄운 진짜 데이터베이스에 붙입니다. 5432 는 그 데이터베이스가 요청을 기다리는 포트 번호입니다. 포트는 한 머신 안에서 어느 프로그램에게 가는 요청인지 가르는 번호입니다.

Java
var repo = new OrderRepository("localhost:5432");
repo.save(new Order(1, 3000));
repo.find(1).price();  // 3000

저장한 주문을 다시 꺼내면 3000 이 나옵니다. 이번에는 가짜 저장소가 아니라 디스크에 적힌 값을 데이터베이스가 읽어 준 것입니다. 쿼리 문법이 틀렸거나 테이블 모양이 코드와 어긋났다면 여기서 깨집니다.

느리고 흔들리는 까닭

이 칸에서 생기는 비용은 두 갈래입니다. 하나는 느려지는 것입니다. 다른 하나는 결과가 흔들리는 것입니다.

느려지는 까닭은 기다림이 생기기 때문입니다. 데이터베이스 프로세스를 띄우는 데 시간이 듭니다. 디스크에 쓰고 읽는 일은 메모리 안의 계산보다 훨씬 오래 걸립니다.

흔들림은 더 성가십니다. 프로세스와 스레드가 여럿이면 누가 먼저 도는지를 운영체제가 정합니다. 그 순서는 매번 달라질 수 있습니다.

같은 입력에 늘 같은 결과를 내는 성질을 결정성이라고 합니다. 실행 순서가 매번 달라지면 테스트는 이 성질을 잃기 쉽습니다.

순서에 따라 결과가 바뀌는 잘못을 경쟁 상태라고 부릅니다. 경쟁 상태가 있으면 코드를 하나도 안 바꿨는데 테스트가 통과했다 실패했다 합니다.

이렇게 코드가 그대로인데 결과가 바뀌는 테스트가 불안정한 테스트입니다. 중간 테스트는 작은 테스트보다 불안정한 테스트가 되기 쉽습니다.

중간 테스트를 쓰는 경우

같은 기능을 확인할 수 있다면 더 작은 테스트를 씁니다. 더 빨리 끝나고 결과도 덜 흔들리기 때문입니다. 중간 테스트는 작은 테스트로 확인할 수 없는 것이 있을 때 고릅니다. 그런 경우는 주로 둘입니다.

첫째는 진짜 데이터베이스와 함께 확인해야 할 때입니다. 메모리에 만든 가짜 저장소는 쿼리를 해석하지 않습니다. 쿼리가 진짜 데이터베이스에서도 통하는지는 진짜를 띄워 붙여 봐야 압니다.

둘째는 웹 화면과 서버 코드를 함께 확인해야 할 때입니다. 테스트 코드가 같은 머신에 진짜 브라우저를 띄우고 사람 대신 조작합니다. 버튼을 누르면 서버까지 요청이 가서 화면이 바뀌는지를 한 번에 봅니다.

개수로 보면 중간 테스트는 작은 테스트보다 적고 큰 테스트보다 많이 둡니다. 개수를 이렇게 쌓으면 아래가 넓은 삼각형이 됩니다. 이 모양을 테스트 피라미드라고 부릅니다.

중간 범위 테스트와 다른 축

중간 테스트와 중간 범위 테스트는 이름이 닮았지만 서로 다른 잣대의 말입니다. 테스트 범위는 테스트가 어느 코드를 확인하느냐를 봅니다. 크기는 테스트가 어디서 도느냐를 봅니다.

두 잣대는 자주 함께 움직입니다. 클래스 몇 개가 데이터베이스와 잘 맞물리는지 확인하는 테스트는 범위가 중간입니다. 그 데이터베이스를 같은 머신에 띄우면 크기도 중간입니다. 그래도 늘 같이 가지는 않습니다.

클래스 하나만 확인하는데 같은 머신의 진짜 데이터베이스에 붙이면 범위는 좁고 크기는 중간입니다. 반대로 시스템 전체를 머신 한 대에 몰아 띄워 처음부터 끝까지 확인하면 범위는 넓고 크기는 중간입니다.

관련 항목

중간 테스트와 같은 크기 잣대로 나뉘는 테스트

테스트 크기 · 작은 테스트 · 큰 테스트 · 허미틱 테스트

중간 테스트와 다른 축인 범위로 나뉘는 테스트

테스트 범위 · 좁은 범위 테스트 · 중간 범위 테스트 · 넓은 범위 테스트 · 단위 테스트 · 통합 테스트 · 종단 간 테스트

중간 테스트가 머신 안에 띄워 붙이는 진짜 부품

데이터베이스 · 프로세스 · localhost · 포트 · 테스트 컨테이너 · 헤드리스 브라우저 · WebDriver

중간 테스트가 잃기 쉬운 성질과 그 증상

결정성 · 경쟁 상태 · 불안정한 테스트 · 테스트 격리 · 스레드 · 블로킹 · 입출력

중간 테스트 대신 쓸 수 있는 수단

테스트 더블 · 페이크 객체 · 인메모리 데이터베이스 · 계약 테스트

중간 테스트의 개수를 정하는 배분 모양

테스트 피라미드 · 테스팅 트로피 · 아이스크림 콘 안티패턴

다른 이름: medium test · medium tests · 중형 테스트