소프트웨어 테스트란 무엇인가?
테스트는 무엇인가가 잘 동작하는지 확인하는 활동입니다. 이러한 컨셉을 소프트웨어에 적용한 것이 바로 '소프트웨어 테스트'입니다. 소프트웨어 테스트는 우리가 만든 소프트웨어가 예상한 대로 동작하는지 확인하는 과정입니다.
과거에는 개발자가 소프트웨어를 만들고, 별도의 테스트 팀이 그 소프트웨어가 제대로 동작하는지 확인하는 구조였습니다. 그러나 현대의 소프트웨어 개발 흐름에서는 개발자가 자동화된 테스트를 작성하고 실행하는 것이 일반적인 상황이 되었습니다.
자동화된 테스트란, 사람이 직접 테스트하는 것이 아닌, 컴퓨터가 대신 테스트하는 것을 의미합니다. 이렇게 컴퓨터를 통한 자동화된 테스트에는 다음과 같은 이점이 있습니다:
- 컴퓨터가 테스트를 수행하므로, 사람이 직접 테스트를 수행하는 것보다 훨씬 빠릅니다.
- 정해진 스크립트에 따라 일관성 있게 동작하기 때문에, 사람이 직접 테스트를 할 때 발생할 수 있는 오류를 방지할 수 있습니다.
개발자가 직접 소프트웨어 테스트를 작성하면, "피드백을 빠른 주기로 개발 중에 받을 수 있다"는 가장 큰 이점이 있습니다. 개발자가 직접 테스트를 실행하면, 작성한 코드가 제대로 동작하는지를 실시간으로 확인할 수 있습니다. 이렇게 빠르게 피드백을 받으면서 코드를 수정해 나가는 과정은, 개발자의 생산성을 향상하는 중요한 요인입니다. 또한, 이렇게 작성한 자동화된 테스트는 CI/CD와 같은 프로세스에서 코드의 정상 작동을 확인하는데 활용할 수 있습니다.
소프트웨어 테스트의 종류
테스트의 확인 대상과 복잡성에 따라, 소프트웨어 테스트는 크게 세 가지로 분류할 수 있습니다.
1) 단위 테스트(Unit Test)
단위 테스트는 테스트의 기본 단위인 '단위'에 대한 테스트를 말합니다. 단위는 소프트웨어의 가장 작은 기능적인 부분을 나타내며, 보통 하나의 함수 또는 메소드를 지칭합니다. 단위 테스트는 이러한 함수 또는 메소드가 예상대로 동작하는지를 확인하는데, 이때 사용하는 데이터는 보통 하드 코딩된 값을 사용합니다.
2) 통합 테스트(Integration Test)
통합 테스트는 여러 단위가 서로 상호작용하면서 원하는 결과를 내는지 확인하는 테스트입니다. 이 테스트는 단위 테스트에서 확인하지 못한 버그를 찾아내는데 효과적입니다. 예를 들어, 서로 다른 두 단위가 잘못 연결되어 있는 경우, 각각의 단위 테스트에서는 문제를 찾아내기 어렵지만 통합 테스트에서는 이런 문제를 찾아낼 수 있습니다.
3) End-to-End Test(E2E Test)
E2E 테스트는 실제 유저가 애플리케이션을 사용하는 것과 유사한 환경을 구축한 후 실제 유저의 동작을 흉내내서 테스트하는 것입니다. 이는 실제 유저의 동작 흐름을 그대로 모방해서 테스트할 수 있다는 장점이 있지만 환경을 구축해야 하며, 유저의 행동 시나리오를 구축해야 하기에 굉장히 비싼 테스트입니다. 따라서 실제 개발 환경에서 유닛테스트와 통합테스트처럼 소스코드에 변화가 있을 때마다 빈번하게 수행할 수는 없으며, 대부분 핵심 기능에 대해서 E2E 테스트를 구축한 후 확인이 필요한 순간에만 실행하는 것이 일반적입니다. 프론트엔드에서의 E2E 테스트는 실제 브라우저와 유사한 환경을 구축한 후, 거기서 실제로 여러 가지 이벤트를 발생시킨 후 일련의 과정을 테스트하는 방식으로 진행됩니다
현재 자바스크립트 지연ㅇ에서 Jest, Mocha, chai 등의 테스트 라이브러리들이 대표적으로 사용되고 있습니다. 이 중에서 Jest가 압도적인 점유율을 가지고 있으며, CRA에서 기본적으로 Jest를 포함해서 환경을 구성하는 등 사실상 표준으로서 사용되기에 Jest를 기준으로 테스트코드를 작성해 보겠습니다.
Jest 사용법
Jest는 기본적으로 *.test.* 형태의 파일을 테스트 파일로 인식하여 해당 파일 내의 코드를 실행합니다. 일반적으로 소프트웨어를 테스트하는 과정은 아래와 같습니다:
- 특정한 동작을 수행합니다.
- 수행한 결과가 기대한 결과와 일치하는지 확인합니다.
테스트 코드를 작성할 때도 이와 동일하게 테스트하고자 하는 동작을 수행한 후 그 결과가 기대한 결과와 일치하는지 검증합니다.
Jest에서는 이를 통해 실제 결과와 기댓값이 일치하는지 확인하는 함수들을 matcher라고 합니다. 따라서 Jest의 코드는 아래와 같은 형태를 가집니다:
- 특정한 동작을 수행합니다.
- matcher를 통해 실제 결과와 기대값이 일치하는지 확인합니다.
이때 test() 또는 it() 함수를 사용하여 하나의 특정한 동작을 수행할 수 있습니다.
아래의 실제 코드를 살펴봅시다:
test('two plus two is four', () => {
expect(2 + 2).toBe(4);
});
it('two plus two is four', () => {
expect(2 + 2).toBe(4);
});
위의 코드처럼 테스트는 test("테스트 이름", callback) 형태를 가지며, callback 내에서 원하는 동작을 수행하고 expect(실제 결과 값).matcher() 형태를 가집니다. 하나의 callback 내에서 여러 expect를 수행할 수 있으며, 그중 하나라도 기댓값과 일치하지 않으면 해당 테스트는 실패한 것으로 간주됩니다.
const sum = (x,y) => x + y;
test('sum', () => {
expect(sum(2,2)).toBe(4); // 통과
expect(sum(3,1)).toBe(5); // 실패, sum test 실패
});
Jest에서 주로 사용되는 matcher들은 다음과 같습니다:
- toBe : expect의 인자가 toBe의 인자와 일치하는지를 검사합니다.
- toEqual : 객체의 경우 참조 값이 다르므로, toBe를 사용하면 실제로 각 객체의 내용이 같더라도 일치하지 않다고 판단됩니다. 따라서 객체를 비교할 때는 toEqual matcher를 사용해야 합니다. toEqual은 객체의 각 요소를 재귀적으로 검사하여 두 객체가 동일한지 판단합니다.
const obj = {hello:"world"};
test("object equal", () => {
expect(obj).toBe({hello:"world"}) // X
expect(obj).toEqual({hello:"world"}) // O
});
- toBeNull, toBeUndefined
- toBeGreaterThan, toBeGreaterThanOrEqual, toBeLessThan, toBeLessThanOrEqual : 숫자 값을 검증할 때 사용하는 matcher입니다.
- toContain : Iterable 한 객체가 특정한 요소를 포함하고 있는지 검증할 때 사용할 수 있습니다.
- jsxCopy code const iterable = [1,2,3,4,5]; test("iterable contains 3", () => { expect(iterable). toContain(3) });
- not : matcher의 기댓값을 반대로 변경해 줍니다.
- test('null', () => { const n = null; expect(n). toBeNull(); expect(n).not.toBeUndefined(); });
RTL에서 제공하는 메서드들
- render: 테스트를 위해 리액트 컴포넌트를 DOM에 렌더링 합니다. render 함수는 테스트를 위해 사용되는 여러 가지 유용한 메서드와 객체를 반환합니다.
- getBy: 테스트를 진행할 때 사용되는 쿼리 메서드입니다. 이 메서드를 사용해 특정 요소를 선택할 수 있습니다. getBy 메서드의 종류에는 여러 가지가 있습니다. 예를 들면 getByText, getByTestId, getByAltText, getByRole 등이 있습니다.
- queryBy: getBy와 동일한 기능을 제공하지만, 차이점이 하나 있습니다. 만약 찾으려는 요소가 없을 경우 getBy는 에러를 발생시키지만 queryBy는 null을 반환합니다. 이 메서드는 주로 조건부 렌더링을 테스트할 때 유용합니다.
- findBy: 비동기적으로 요소를 찾습니다. 이 메서드는 주로 비동기 작업이 완료된 후에 요소가 렌더링 되는 것을 테스트할 때 사용됩니다.
- expect: Jest의 기본 함수로, 테스트 케이스의 결과를 검증하는 데 사용됩니다. 보통 toBe, toEqual, toContain 등과 함께 사용됩니다.
- cleanup: 각 테스트가 끝난 후 렌더링된 UI를 정리합니다. RTL에서는 기본적으로 각 테스트 사이에 이를 자동으로 호출하기 때문에 수동으로 호출할 필요는 없습니다.
Jest DOM
React Testing Library와 함께 사용되는 jest-dom 라이브러리는 테스트를 더 효과적으로 수행할 수 있도록 Jest의 기능을 확장합니다. jest-dom은 커스텀 matcher를 제공하여 DOM 요소의 상태를 더 쉽게 검증할 수 있습니다. 예를 들어, 특정 엘리먼트가 DOM에 존재하는지 확인하거나, 엘리먼트가 특정 텍스트를 가지고 있는지 확인하는 작업을 할 수 있습니다.
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';
it('renders without crashing', () => {
render(<MyComponent />);
expect(screen.getByText('Hello World')).toBeInTheDocument();
});
위 코드에서 toBeInTheDocument는 jest-dom에서 제공하는 커스텀 matcher입니다. 이는 선택한 요소가 현재 문서에 존재하는지 확인합니다. 다른 커스텀 matcher로는 toBeVisible, toBeDisabled, toHaveTextContent 등이 있습니다.
이와 같이 Jest와 React Testing Library를 함께 사용하면, 실제 사용자 행동을 잘 반영한 테스트를 작성할 수 있습니다. 이를 통해 애플리케이션의 안정성을 높이는데 도움이 될 것입니다.
'FrontEnd > Test' 카테고리의 다른 글
React Testing Library(RTL)와 Enzyme에 대한 나의 생각 (0) | 2023.06.22 |
---|