Dev/General
[소프트웨어 테스트] 자동화 테스트와 유닛 테스트
싯벨트
2022. 9. 4. 21:52
728x90
1. 소프트웨어 테스트
1-1. 정의
소프트웨어를 배포하기 전에 정상적으로 작동하는가에 대한 검증하는 단계
- 노출되지 않은 결함과 오류 발견을 통한 품질(동작, 성능, 안정성) 평가 및 개선
1-2. 분류
Did we build it right? (올바르게 코드를 짰는가?)
1) 단위 테스트(Unit Test)
- 함수 및 클래스 단위로 진행할 수 있기에 가장 많은 수를 차지
- 모든 코드의 단위 테스트가 가능하다는 것(Testable)은 독립적으로 테스트를 통해 다른 단위에 영향을 주지 않으면서 안전하게 테스트 하며 개발품질을 높일 수 있는, 즉 모듈화가 잘 되었다고 볼 수 있음
2) 통합 테스트(Integration Test)
- 단위 테스트의 단위(모듈)들을 조합하여 테스트를 구성
- 상향식 통합 테스트: 존재하는 모듈이 통합되었을 때 잘 동작하는지 테스트
- 하향식 통합 테스트: 하위 모듈이 있는 것을 가정하고 상위 모듈을 테스트
Did we build the right thing? (코드가 올바르게 동작하는가?)
3) 인수 테스트(Acceptance Test)
- 통합 테스트에서 더 나아가 실제 환경처럼 세팅하어 테스트
- 알파 테스트: 통제된 환경에서 선별된 사용자들(사내 직원 등)이 개발자와 함께 수행하는 테스트
- 베타 테스트: 실제와 동일한 환경에서 사외의 사용자들이 테스트하게 하고 피드백을 받는 방법
1-3. 코드 커버리지
1) 정의
테스트가 코드를 얼마나 커버하는지에 대한 정도
2) 분류
함수(function), 구문(statement), 조건(condition), 분기(branch)
- 함수 커버리지 (함수가 실행되지 않는다면 다른 커버리지 또한 측정을 할 수 없음)
함수 커버리지(%) = 실행된 함수 개수 / 총 함수 개수 * 100
- 구문 커버리지 (Javascript 에서 보통 세미 콜론(;)으로 구분됨)
구문 커버리지(%) = 실행된 구문 수 / 전체 구문 수 * 100
- 조건 커버리지 (전체 조건 = 조건 개수 ^ 2)
조건 커버리지(%) = 실행된 경우의 수 / 전체 조건 수 * 100
- 분기 커버리지 (분기 - 조건으로 인하여 나뉘게 되는 실행 경로)
분기 커버리지(%) = 실행된 분기 수 / 총 분기의 개수 * 100
1-4. 소프트웨어 테스트 역사
By Gelperin과 Hetzel의 진화적 테스팅 모델 개념
- 디버깅 지향 시대 (debugging-oriented period) - 버그 제거
- 증명 지향 시대 (demonstration-oriented period) - 요구사항 구현 포함
- 파괴 지향 시대 (destruction-oriented period) - 오류 발견(테스트) 후 디버깅
- 평가 지향 시대 (evaluation-oriented period) - 개발 생명 주기에서 검토 역할
- 예방 지향 시대 (Prevention-oriented Period) - 문제를 미리 찾아냄
2. 테스트 자동화
2-1. TDD(Test Driven Develpment) - 테스트 주도 개발
테스트 코드가 설계의 역할(애자일한 개발 접근법)
- 테스트코드를 먼저 작성
- 하드 코딩하여 재빠르게 기능을 구현
- 리팩토링하여 성능 및 안정성을 높입니다.
2-2. 테스트 자동화의 중요성
개발팀의 생산성 향상과 시스템의 안정적 운영 추구
- 휴먼 에러 제거
- 정확성 유지
- 반복적 테스트 실행 가능
- 빠지는 부분없이 테스트 실행 가능
단위 테스트 자동화 필수 구현
디버깅 하기 쉽고 100% 자동화가 가능하기 때문에 시스템 테스트 전략중 가장 많은 비율을 차지(70%)
3. 테스트 자동화 실습
3-1. 유닛 테스트 실습
테스트 코드 구성
구성 | 형태 | 세부 설명 |
describe 부분 | describe (description, callback) | # description: 해당 부분에 대한 카테고리 및 설명 # callback 내부: - 다시 describe 및 test 를 선언가능 - expect를 통해 값을 비교하여 정상적으로 수행했는지 확인 가능 - 하나의 테스트에 여러개의 expect 가능 |
test 부분 |
# 새로운 폴더에서 한다면 $ npm init -y 로 package.json 설치부터
$ npm i -D jest
# npx 명령어를 수행하면 프로젝트 내에 설치된 jest가 *.test.js 나 *.spec.js 같은 테스트 코드 파일들을 찾아 자동으로 실행
$ npx jest
테스트 코드 예제 - 사칙연산 계산기
// calculate.js
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
function multiple(a, b) {
return a * b;
}
function divide(a, b) {
return a / b;
}
module.exports = { add, subtract, multiple, divide }
// calculate.test.js
const { add, subtract, multiple, divide } = require('./calculate');
describe('calculate unit test', () => {
describe("add", () => {
test("add(1,2)는 3이 되어야합니다.", () => {
expect(add(1, 2)).toBe(3);
});
});
describe("subtract", () => {
test("subtract(2,1)는 1이 되어야합니다.", () => {
expect(subtract(2, 1)).toBe(1);
});
});
describe("multiple", () => {
test("multiple(3, 4)는 12가 되어야합니다.", () => {
expect(multiple(3, 4)).toBe(12);
});
});
describe("divide", () => {
test("divide(10, 2)는 5가 되어야합니다.", () => {
expect(divide(10, 2)).toBe(5);
});
});
});
3-2. 통합 테스트 실습
더하기 결과와 빼기 결과를 곱하는 과정
// calculate.test.js
const { add, subtract, multiple, divide } = require('./calculate');
...기존 unit test
/// 통합 테스트 추가
describe("calculate integration test", () => {
test("add(1, 3)과 subtract(4, 2)를 multiple한 결과는 8입니다.", () => {
const addResult = add(1, 3);
expect(addResult).toBe(4);
const subtractResult = subtract(4, 2);
expect(subtractResult).toBe(2);
expect(multiple(addResult, subtractResult)).toBe(8);
});
});
3-3. 테스트 커버리지 확인
npx jest --coverage
분기 만들고 test 추가하기
calculate.js add함수에 if문을 통한 분기 함수 추가
function add(a, b) {
if (b < 0) {
throw new Error("b가 0보다 작습니다. subtract를 사용하세요!");
}
return a + b;
}
커버리지 확인 결과 - 분기(Branch 커버리지 50%)
해당 분기에 대한 테스트 추가
- expect(err).toBeInstanceOf(Error) // Error Class의 인스턴스인지 확인
- expect(err).toHaveProperty("message", "b가 0보다 작습니다. subtract를 사용하세요!"); // throw된 err객체의 프로퍼티(키벨류)가 일치하는지 확인
describe("add", () => {
test("add(1, 2)는 3이 되어야합니다.", () => {
expect(add(1, 2)).toBe(3);
});
// 아래 테스트 추가
test("add(1, -1)은 에러를 던집니다.", () => {
try {
add(1, -1);
} catch (err) {
expect(err).toBeInstanceOf(Error)
expect(err).toHaveProperty("message", "b가 0보다 작습니다. subtract를 사용하세요!");
}
});
});