테스팅의 종류
소프트웨어 공학 이론에서 많은 기준의 테스트를 제시하고 있다. 분류 방법에 따라 나뉘는 기준도 달라질 수 있다.
종류는 크게 테스트 수준, 테스트 유형, 테스트 접근 방식에 따른 분류로 나뉠 수 있다.
테스트 수준 분류
소프트웨어 개발 과정 각 단계에 따라 실행되는 테스트 종류
- 단위 테스트
- 통합 테스트
- 시스템 테스트
- 인수 테스트
단위 테스트와 통합 테스트 이 두 가지를 어떻게 다뤄야 할지 집중적으로 공부할 예정
단위 테스트
클래스나 함수 단위의 프로그램을 원하는 대로 동작하는지 확인
시스템의 가장 작은 코드 단위를 테스트하는 목적
- 대상: 함수, 메서드, 클래스
- 수행 시점: 개발 초기 단계 혹은 새로운 클래스나 메서드를 작성할 때
- 수행자: 개발자
- 자동화 도구: JUnit (Java 기준)
통합 테스트
단위 테스트에서 검증한 프로그램을 합쳐서 실행
모듈 간의 상호작용 및 데이터 흐름 확인
- 대상: 여러 모듈, API 연동, 서비스 로직
- 수행 시점: 개발 중간 및 이후 단계, 모듈 간 의미 있는 상호 동작이 될 때
- 수행자: 개발자, 테스트 팀
- 자동화 도구: JUnit, Postman 등
시스템 테스트
전체 시스템이 요구사항을 충족하는지 확인
사용자 환경과 유사한 조건에서 테스트
- 도구: Selenium 웹 테스트, JMeter 성능 테스트 등
인수 테스트
제품 릴리스 전 요구사항이 충족되었는지 최종 사용자가 확인
시나리오 기반 테스트
테스트 유형 분류
테스트의 목적에 따른 분류
- 기능 테스트
- 비기능 테스트
- 회귀 테스트
- 탐색적 테스트
기능 테스트
기능 요구사항에 맞게 동작하는지 확인하는 테스트
기능별 테스트 케이스를 실행
성능 테스트
시스템의 반응 속도, 안정성을 확인하는 테스트
부하 테스트 실행
보안 테스트
시스템의 취약점을 식별하는 테스트
- 도구: SonarQube, Burp Suite, vega, SQLMap 등
사용성 테스트
UI/UX 사용 편의성 평가
회귀 테스트
코드를 변경한 후에 테스트 케이스를 재사용하여 기존 기능이 정상적으로 동작하는지 확인
탐색적 테스트
테스트 설계 없이 자유롭게 시스템을 탐색하며 결함 발견
테스트 접근 방식 분류
테스트를 설계하고 수행하는 방식에 따라 분류
- 화이트박스 테스트
- 블랙박스 테스트
- 그레이박스 테스트
화이트박스 테스트
코드 내부 구조와 로직을 기반으로 테스트
- 단위 테스트, 경로 테스트, 조건 테스트가 해당
- 예시:
if 문이나 for 루프의 모든 분기 조건을 검증
블랙박스 테스트
내부 구현을 모른 채 외부 입력과 출력만 테스트
- 기능 테스트, 비기능 테스트가 해당
- 예시: 특정 입력값에 대해 예상 출력값 확인
그레이박스 테스트
내부 구조를 부분적으로 이해한 상태에서 테스트
테스팅 방법론
TDD란?
TDD(Test Driven Development), 테스트 주도 개발
- 테스트 코드를 먼저 작성하고 코드를 구현하는 것을 반복적으로 개선하는 개발 방식
- 짧은 개발 사이클에 사용됨(애자일 등)
- 코드 품질 향상, 변경에 강한 구조 설계 목표(유지보수, 확장, 리팩토링 용이)
- 테스트 코드가 기능 명세를 대체
TDD 프로세스
주로 다음 Red-Green-Refactor 주기를 반복
- Red(실패)
- 구현되지 않은 기능의 테스트 코드 작성(구현되지 않았기 때문에 실패 테스트)
- Green(성공)
- 빠른 테스트 통과를 위한 최소한의 코드를 작성
- Refactor(코드 개선)
TDD 단점
- 시간 소모: 초기 개발 속도에 영향
- 복잡성 증가: 테스트 코드 작성의 어려움
- UI 중심 애플리케이션은 적합하지 않음
BDD란?
BDD(Behavior Driven Development), 행위 주도 개발
TDD(Test Driven Development) + 테스트 케이스 자체가 요구사항이 되도록 하는 개발 방법
(요구사항 = 테스트 케이스)
- 시나리오(사용자 행동)를 기반으로 테스트 케이스를 작성
- 비개발자(기획자, QA, 고객)가 이해할 수 있는 언어(자연어 기반)로 작성
- 사용자 입장에서 사용하는 방식을 중심으로 작성
- 개발자 <-> 비개발자 소통 중심, 소통 강화
- 도구: Cucumber, JBehave
BDD 작성 패턴
Given-When-Then 구조로 테스트 정의
- Given(상황): 시나리오에 필요한 값 (예: 로그인 화면에 접속한 상태에서)
- When(행동): 시나리오 진행 시 필요한 조건 (예: 올바른 이메일, 비밀번호를 입력하고 로그인 버튼을 누르면)
- Then(결과): 시나리오 완료 시 보장되는 결과 (예: 대시보드 화면이 표시됨)
그 외
- Feature: 테스트에 대상의 기능/책임
- Scenario: 테스트 목적 상황을 설명
BDD 단점
- 초기 도입 비용: BDD 도구 학습, 설정
- 테스트 관리의 어려움: 프로젝트가 커질 수록 많은 시나리오에 따라 중복 테스트가 발생할 수도 있고, 요구사항 변경 시 시나리오를 전부 수정해야함
- 추상화의 한계: 복잡한 비즈니스 로직은 매우 긴 시나리오로 이어질 수 있음, 기술적인 로직(데이터베이스 트랜잭션 등)을 자연어로 설명하는 데 제한적
- 상대적으로 느린 테스트 속도: 사용자 중심으로 UI 연관 테스트가 많아 테스트 속도가 느릴 수 있음
- 비개발자와 협업 한계: 기술 지식 격차로 인한 의사소통 오류. 시나리오가 추상적이라 비개발자가 테스트 요구사항을 반영한다고 믿을 수 있지만, 실제 구현은 다를 수 있음
- 테스트 복잡성 증가: 간단한 기능 테스트도 시나리오로 작성하면 장황해질 수 있음
- 예외 처리 누락 가능성 존재: 예로, '로그인 성공'은 처리했지만 '로그인 실패' 상황은 없음
등이 존재
위 단점들을 커버하기 위해 다음의 노력이 필요
- 팀 전체의 BDD 도구와 프로세스에 대한 이해
- 테스트 시나리오의 재사용성과 간결성을 유지
- 기술적 문제와 사용자 요구사항 간의 균형을 맞추는 작업
테스트 커버리지
- 코드 커버리지: 작성한 테스트 코드가 실제 소스 코드의 몇%를 실행했는지 측정
- 기능 커버리지: 구현된 기능이 테스트에서 얼마나 이루어졌는지
- 경로 커버리지: 코드의 모든 실행 경로가 테스트되었는지
테스트 프로세스
- 테스트 계획 작성: 테스트의 목표, 범위, 방법 등 문서화
- 테스트 케이스 설계: 다양한 입력값, 시나리오 고려
- 테스트 실행: 자동화 도구를 사용하거나 수동으로 테스트를 진행
- 결과 분석 및 버그 리포트
개발자가 흔히 말하는 "테스트 코드를 써야한다"
테스트 코드는 애플리케이션 코드를 검증하기 위해 별도로 작성하는 코드를 말한다.
이 코드는 주어진 테스트 케이스에 대해 자동화 도구를 사용하여 의도한 대로 동작하는지 확인하는 데 필요하다.
테스트 코드는 왜 필요할까?
목적은 다음과 같다.
- 검증: 프로그램이 올바르게 동작하는지 확인하기 위해
- 보장: 새로운 기능 추가, 수정에도 기존 코드가 문제없이 동작하는지 보장하기 위해
- 자동화: 반복적인 수동 테스트를 줄여 빠르고 일관된 검증 제공 가능
취할 수 있는 이점은 다음과 같다.
(테스트 장점 + 테스팅 자동화 장점 + 코드 문서화 장점)
- 버그 조기 발견: 코드가 의도대로 동작하는지 확인하여 버그를 미리 발견
- 품질 보장: 기능의 정확성과 일관성을 보장
- 유지보수와 확장, 리팩터링 용이: 변경의 위험이 줄어 안정적인 개발 가능(테스트 코드를 통해 코드 검증과 보장이 되어 기존보다 빠르게 문제를 확인하고 대처할 수 있음)
- 회귀 방지: 새로운 기능을 추가할 때 기존의 기능이 영향받지 않도록 검증
- 개발 속도 향상: 자동화되어 빠른 결과 확인이 가능
- 문서화 역할: 코드의 예상 동작을 보여줌
테스트 코드 작성
TDD, BDD 방법론을 개발에 적용하기 위해 테스트코드를 작성해야 한다.
Java를 기준으로 테스트코드를 작성할 때 내가 알고 넘어가야 하는 것들을 정리하였다.
작성 기본 구조
테스트코드는 일반적으로 AAA(Arrange, Act, Assert) 패턴으로 작성
- Arrange: 테스트에 필요한 초기 설정
- Act: 테스트할 코드 실행
- Assert: 결과가 예상과 같은지 검증
자바에서 자주 사용되는 자동화 도구
- JUnit5(JUnit Jupiter): 자바 테스트 프레임워크. 단위, 통합 테스트 애너테이션 지원
- AssertJ: 체이닝 어설션 제공
- Hamcrest: 어설션 매처(assertThat 메서드 등) 제공
- MockMvc: 스프링 MVC의 컨트롤러 테스트 도구. HTTP 요청/응답 시뮬레이션 제공
- Mockito: 모킹 제공
- Spring Boot Test: 스프링 부트 전용 테스트 도구 모음(위 라이브러리 포함 JSONassert, Spring Security Test, H2 Database 등 많은 라이브러리 모음집), 애너테이션 지원
Mock이란?
Mock은 테스트 대상 코드에서 외부 의존성을 대체하기 위해 필요한 가짜 객체다.
실제 객체를 호출하지 않고 예상 동작을 시뮬레이션할 수 있다.
- 외부 의존성을 제거 (예시: DB, 외부 API 호출 mocking)
- 단위 테스트에 자주 사용됨
- 단위 테스트 외에도 일부 의존성을 제거하거나 원하는 환경을 인위적으로 만들 때 사용할 수 있음
- 테스트 속도와 안정성이 향상됨 (외부로 인한 버그 가능성 감소)
Mockito
Java에서 Mockito 라이브러리로 Mock을 쉽게 구현가능하다.
Mock 의존성 주입을 위한 주요 애노테이션
@Mock Mock 객체 생성
@InjectMock 주입이 필요한 곳에 Mock 객체를 주입할 때 사용
Stub이란?
- 상태(값) 검증에 중점을 둔 테스트
- 어떤 시나리오에서 반드시 나와야 하는 결과 값을 설정
- Mockito의
when() 메서드 사용하여 stubbing
- 고정된 값을 반환하는 테스트이므로 이에 맞지 않는 동작 시뮬레이션은 어려움
버그 리포트 작성
- 버그 발생 환경 (OS, 브라우저, 버전 등)
- 버그 재현 단계 (step-by-step)
- 기대 결과, 실제 결과
- 관련 로그 및 스크린샷
참고하면 좋은 도서
테스트 코드 작성과 좋은 실천법 기술
"Clean Code" - Robert C. Martin