- 단위 테스트
- 통합 테스트
- 시스템 레벨의 화이트박스 테스트 (이전에는 시스템/기능 테스트로 알려졌음)
- 시스템 수준의 블랙박스 테스트(End-to-end 테스트)
- EE(Enterprise Edition) 특정 테스트
- 올바른 수준에서 테스트하는 방법?
테스트 수준
이 다이어그램은 사용하는 각 테스트 유형의 상대적 우선 순위를 보여줍니다. e2e
는 end-to-end의 약자입니다.
2019-05-01 기준, 다음과 같은 테스트 분배가 있습니다:
테스트 수준 | 커뮤니티 에디션 | 엔터프라이즈 에디션 | 커뮤니티 + 엔터프라이즈 에디션 |
---|---|---|---|
시스템 수준의 블랙박스 테스트 (또는 end-to-end 또는 QA 테스트) | 68 (0.14%) | 31 (0.2%) | 99 (0.17%) |
시스템 수준의 화이트박스 테스트 (또는 시스템 또는 기능 테스트) | 5,471 (11.9%) | 969 (7.4%) | 6440 (10.9%) |
통합 테스트 | 8,333 (18.2%) | 2,244 (17.2%) | 10,577 (17.9%) |
단위 테스트 | 32,031 (69.7%) | 9,778 (75.1%) | 41,809 (71%) |
단위 테스트
공식적인 정의: https://en.wikipedia.org/wiki/Unit_testing
이 유형의 테스트는 코드의 단일 단위(메소드)가 기대한 대로 작동하는지를 보장합니다(입력이 주어지면 예측 가능한 출력이 있어야 합니다). 이러한 테스트는 최대한 독립적으로 작성되어야 합니다. 예를 들어 데이터베이스와 아무 상관이 없는 모델 메소드는 DB 레코드가 필요하지 않아야 합니다. 데이터베이스 레코드가 필요하지 않은 클래스는 최대한 많은 부분을 stubs/doubles로 사용해야 합니다.
코드 경로 | 테스트 경로 | 테스트 엔진 | 비고 |
---|---|---|---|
app/assets/javascripts/
| spec/frontend/
| Jest | 자세한 내용은 Frontend Testing 가이드 섹션을 참조하세요. |
app/finders/
| spec/finders/
| RSpec | |
app/graphql/
| spec/graphql/
| RSpec | |
app/helpers/
| spec/helpers/
| RSpec | |
app/models/
| spec/models/
| RSpec | |
app/policies/
| spec/policies/
| RSpec | |
app/presenters/
| spec/presenters/
| RSpec | |
app/serializers/
| spec/serializers/
| RSpec | |
app/services/
| spec/services/
| RSpec | |
app/uploaders/
| spec/uploaders/
| RSpec | |
app/validators/
| spec/validators/
| RSpec | |
app/views/
| spec/views/
| RSpec | |
app/workers/
| spec/workers/
| RSpec | |
bin/
| spec/bin/
| RSpec | |
config/
| spec/config/
| RSpec | |
config/initializers/
| spec/initializers/
| RSpec | |
config/routes.rb , config/routes/
| spec/routing/
| RSpec | |
config/puma.example.development.rb
| spec/rack_servers/
| RSpec | |
db/
| spec/db/
| RSpec | |
db/{post_,}migrate/
| spec/migrations/
| RSpec | 자세한 내용은 Testing Rails migrations 가이드를 참조하세요. |
Gemfile
|
spec/dependencies/ , spec/sidekiq/
| RSpec | |
lib/
| spec/lib/
| RSpec | |
lib/tasks/
| spec/tasks/
| RSpec | |
rubocop/
| spec/rubocop/
| RSpec | |
spec/support/
| spec/support_specs/
| RSpec |
프론트엔드 단위 테스트
단위 테스트는 일반적으로 사용자가 직접 인지하지 못하는 기능을 테스트하는 가장 낮은 추상화 수준입니다.
단위 테스트 사용 시기
- Exported functions and classes: Export된 모든 것은 각기 다른 곳에서 재사용될 수 있으며 그 방식을 제어할 수없는 방법으로 사용될 수 있습니다. 퍼블릭 인터페이스의 예상 동작을 테스트해야 합니다.
- Vuex actions: 모든 Vuex 액션은 트리거된 컴포넌트와는 관계없이 일관된 방식으로 작동해야 합니다.
- Vuex mutations: 복잡한 Vuex 변이에 대해 다른 부분의 테스트를 분리하여 문제 해결을 단순화해야 합니다.
단위 테스트 때 사용하지 말아야 하는 경우
- Export되지 않은 함수 또는 클래스: 모듈에서 export되지 않은 모든 것은 비공개이거나 구현 세부 정보로 간주될 수 있으며 테스트할 필요가 없습니다.
- 상수: 상수의 값을 테스트하는 것은 값을 복사하여 잘못된 추가 노력을 초래하므로 추가적인 확신을 가져다주지 않습니다.
- Vue 컴포넌트: 계산된 속성, 메소드 및 라이프사이클 훅은 컴포넌트 테스트에서 암시적으로 다루어지므로 테스트할 필요가 없습니다. 더 많은 정보는 공식 Vue 가이드를 참조하세요.
단위 테스트에서 모의(mock)할 대상
- 테스트 대상 클래스의 상태: 테스트 설정에서 부작용을 피하려면 클래스의 상태를 직접 수정하는 대신 클래스의 메서드를 사용하세요.
- 다른 내보낸 클래스: 각 클래스는 테스트 시나리오가 기하급수적으로 증가하는 것을 방지하기 위해 격리되어야 합니다.
- 단일 DOM 요소(매개변수로 전달된 경우): 전체 페이지가 아닌 단일 DOM 요소에서만 작동하는 테스트의 경우, 전체 HTML 장치를 로드하는 것보다 이러한 요소를 생성하는 것이 더 저렴합니다.
- 모든 서버 요청: Frontend 단위 테스트를 실행할 때 백엔드에 연결할 수 없을 수 있으므로 모든 외부 요청은 모의화되어야 합니다.
- 비동기 백그라운드 동작: 백그라운드 작업은 중지되거나 대기할 수 없으므로 이후 테스트에서 계속 실행되어 부작용을 일으킵니다.
단위 테스트에서 모의(mock)하지 말아야 하는 대상
- 내보내지 않은 함수 또는 클래스: 내보내지 않은 모든 것은 모듈에 대해 비공개로 간주될 수 있으며, 암시적으로 내보낸 클래스 및 함수를 통해 테스트됩니다.
- 테스트 대상 클래스의 메서드: 테스트 대상 클래스의 메서드를 모의화하면, 모의(mock)가 테스트되고 실제 메서드는 테스트되지 않습니다.
- 유틸리티 함수(순수 함수 또는 매개변수만 수정하는 함수): 상태가 없으므로 부작용이 없는 함수는 테스트하지 않아도 안전합니다.
- 전체 HTML 페이지: 단위 테스트에서 전체 페이지의 HTML을 로드하는 것은 테스트 속도를 늦추므로 피해야 합니다.
Frontend 컴포넌트 테스트
컴포넌트 테스트는 사용자가 외부 신호에 따라 인식할 수 있는 단일 컴포넌트의 상태를 다룹니다. 이때 외부 신호는 사용자 입력, 다른 컴포넌트로부터 발생한 이벤트 또는 애플리케이션 상태입니다.
컴포넌트 테스트에 사용하는 경우
- Vue 컴포넌트
컴포넌트 테스트에 사용하지 않는 경우
- Vue 애플리케이션: Vue 애플리케이션에는 많은 컴포넌트가 포함될 수 있습니다. 그러므로 컴포넌트 수준에서 테스트하는 것은 너무 많은 노력을 필요로 합니다. 따라서 프론트엔드 통합 수준에서 테스트됩니다.
- HAML 템플릿: HAML 템플릿에는 마크업만 포함되어 있고 프론트엔드 쪽 논리가 없습니다. 따라서 완전한 컴포넌트가 아닙니다.
컴포넌트 테스트에서 모의(mock)해야 하는 대상
- 부작용: 외부 상태를 변경할 수 있는 모든 것(예: 네트워크 요청)은 모의화되어야 합니다.
-
하위 컴포넌트:
각 컴포넌트는 개별적으로 테스트되므로 하위 컴포넌트가 모의화됩니다.
또한
shallowMount()
를 참조하세요.
컴포넌트 테스트에서 모의(mock)하지 말아야 하는 대상
- 테스트 대상 컴포넌트의 메서드 또는 계산된 속성: 테스트 대상 컴포넌트의 일부를 모의화하면 모의(mock)가 테스트되고 실제 컴포넌트는 테스트되지 않습니다.
- Vuex: 취약하고 잘못된 양성 테스트를 피하기 위해 Vuex를 모의화하지 마세요. 뮤테이션을 사용하여 Vuex를 적합한 상태로 설정하세요. Vuex 액션을 모의(mock)화하지 말고 부작용을 모의화하세요.
통합 테스트
공식적인 정의: https://en.wikipedia.org/wiki/Integration_testing
이러한 종류의 테스트는 애플리케이션의 각 부분이 실제 앱 환경(예: 브라우저)의 오버헤드 없이 잘 작동하는지 확인합니다. 이러한 테스트는 요청/응답 수준에서 단언해야 합니다: 상태 코드, 헤더, 본문. 예를 들어 권한, 리디렉션, API 엔드포인트, 렌더링된 보기 등을 테스트하는 데 유용합니다.
코드 경로 | 테스트 경로 | 테스트 엔진 | 비고 |
---|---|---|---|
app/controllers/
|
spec/requests/ , spec/controllers
| RSpec | 레거시 컨트롤러 스펙보다는 요청 스펙이 선호됩니다. API 엔드포인트에 대해 요청 스펙이 권장됩니다. |
app/mailers/
| spec/mailers/
| RSpec | |
lib/api/
| spec/requests/api/
| RSpec | |
app/assets/javascripts/
| spec/frontend/
| Jest | 아래 더 자세한 내용 |
프론트엔드 통합 테스트
통합 테스트는 단일 페이지의 모든 컴포넌트 간 상호작용을 다룹니다. 이와 비슷한 추상화 수준은 사용자가 UI와 상호작용하는 방식과 유사합니다.
통합 테스트를 사용해야 하는 시점
-
페이지 번들 (
app/assets/javascripts/pages/
의index.js
파일): 페이지 번들을 테스트하여 해당 프론트엔드 컴포넌트가 잘 통합되는지 확인합니다. - 페이지 번들 외의 Vue 애플리케이션: Vue 애플리케이션을 전체적으로 테스트하여 해당 프론트엔드 컴포넌트가 잘 통합되는지 확인합니다.
통합 테스트에서 모의하기 위해 선택하는 것
- HAML 뷰 (픽스처를 사용합니다): HAML 뷰를 렌더링하려면 실행 중인 데이터베이스를 포함한 Rails 환경이 필요한데, 이는 프론트엔드 테스트에서 의존할 수 없습니다.
- 모든 서버 요청: 유닛 및 컴포넌트 테스트와 유사하게, 컴포넌트 테스트를 실행하는 동안 백엔드에 연결할 수 없을 수 있으므로 모든 외부 요청은 모의해야 합니다.
- 페이지에서 인지할 수 없는 비동기 백그라운드 작업: 페이지에 영향을 주는 백그라운드 작업은 이 수준에서 테스트해야 합니다. 다른 모든 백그라운드 작업은 중단되거나 대기할 수 없으므로 다음 테스트에서 계속 실행되어 부작용을 일으킵니다.
통합 테스트에서 모의하지 않아야 하는 것
- DOM: 실제 DOM에서 테스트하면 컴포넌트가 의도한 환경에서 작동하는지 확인할 수 있습니다. DOM 테스트의 일부는 크로스 브라우저 테스트로 위임됩니다.
- 컴포넌트의 속성 또는 상태: 이 수준에서 모든 테스트는 사용자가 수행할 작업만 수행할 수 있습니다. 예: 컴포넌트의 상태를 변경하려면 클릭 이벤트를 발생시켜야 합니다.
- Vuex 리포지터리: 페이지의 프론트엔드 코드를 테스트할 때 Vue 컴포넌트와 Vuex 리포지터리의 상호작용을 커버합니다.
컨트롤러 테스트에 관해
GitLab은 컨트롤러 스펙에서 요청 스펙으로 전환하고 있습니다.
이상적으로 컨트롤러는 얇아야 합니다. 그러나 그렇지 않은 경우, 컨트롤러 테스트 대신 JavaScript 없이 시스템 또는 기능 테스트를 작성하는 것이 허용됩니다. 뚱뚱한 컨트롤러를 테스트하는 경우에는 다음과 같은 대량의 스터빙이 필요합니다.
controller.instance_variable_set(:@user, user)
및 Rails 5에서 폐지된 메서드를 사용합니다.
시스템 레벨의 화이트박스 테스트 (이전에는 시스템/기능 테스트로 알려졌음)
공식적인 정의:
이러한 종류의 테스트는 GitLab Rails 애플리케이션이 브라우저 관점에서 예상대로 작동하는지 확인합니다.
다음을 주의하세요:
- 애플리케이션 내부에 대한 이해가 여전히 필요합니다.
- 테스트에 필요한 데이터는 보통 RSpec 팩토리를 사용하여 직접 생성됩니다.
- 예상은 대개 데이터베이스나 객체 상태에 설정됩니다.
이러한 테스트는 다음 상황에서만 사용해야 합니다.
- 테스트하는 기능/컴포넌트가 작을 때
- 객체/데이터베이스의 내부 상태를 테스트해야 할 때
- 더 낮은 수준에서 테스트할 수 없을 때
예를 들어, 특정 페이지의 브레드크럼을 테스트하려면 시스템 테스트를 작성하는 것이 의미가 있습니다. 작은 컴포넌트이기 때문에 이를 유닛이나 컨트롤러 수준에서 테스트할 수 없습니다.
오직 성공적인 경로만 테스트하되, 더 나은 테스트를 통해 포착할 수 없었던 회귀의 경우에는 낮은 수준에서 회귀 테스트를 추가하세요 (예: 회귀가 발견되면 가능한 한 낮은 수준에서 회귀 테스트를 추가해야 합니다).
테스트 경로 | 테스트 엔진 | 비고 |
---|---|---|
spec/features/
| Capybara + RSpec | 테스트에 :js metadata가 있는 경우 브라우저 드라이버는 Selenium를 사용하고, 그렇지 않으면 RackTest를 사용합니다.
|
프론트엔드 기능 테스트
프론트엔드 통합 테스트와 대조적으로, 기능 테스트는 픽스처를 사용하는 대신 실제 백엔드에 요청을 보냅니다. 이는 또한 데이터베이스 쿼리가 실행되어 이 범주가 현저히 느리게 만듭니다.
더 읽어보기:
시스템 테스트 작성을 고려하지 않는 것을 고려해보세요.
만약 저수준 컴포넌트가 잘 작동한다고 확신한다면(특히 충분한 단위 및 통합 테스트가 있다면), 시스템 테스트 수준에서 그들의 철저한 테스트를 중복할 필요가 없을 것입니다.
테스트를 추가하는 것은 매우 쉽지만, 제거하거나 테스트를 개선하는 것은 훨씬 어렵기 때문에 (느리고 중복된) 테스트를 너무 많이 도입하지 않도록 주의해야 합니다.
이러한 모범 사례를 따를 이유는 다음과 같습니다:
- 시스템 테스트는 전체 응용프로그램 스택을 headless 브라우저에서 실행하기 때문에 실행 속도가 느립니다. 특히 JS 드라이버를 통합하는 경우 더욱 더 느립니다.
- 시스템 테스트가 JavaScript 드라이버로 실행될 때, 테스트는 응용프로그램과는 다른 스레드에서 실행됩니다. 이는 데이터베이스 연결을 공유하지 않으며, 테스트는 데이터를 확인하기 위해 트랜잭션을 커밋해야 하며 (그 반대도 마찬가지), 이 경우 다른 종류의 테스트에 사용되는 더 빠른 전략인 트랜잭션 롤백 대신 각 스펙 이후에 데이터베이스를 잘라야 합니다. 그러나 이 방법은 트랜잭션보다는 느리기 때문에 필요할 때에만 잘라야 합니다.
시스템 수준의 블랙박스 테스트(End-to-end 테스트)
공식 정의:
GitLab은 GitLab Shell, GitLab Workhorse, Gitaly, GitLab Pages, GitLab Runner 및 GitLab Rails와 같은 여러 부분들로 구성됩니다. 이러한 부분들은 Omnibus GitLab에 의해 구성되고 패키징됩니다.
QA 프레임워크 및 인스턴스 수준 시나리오는 항상 코드베이스(특히 뷰)와 동기화되도록 GitLab Rails의 일부로 구성됩니다.
다음 사항을 주의하세요:
- 응용프로그램 내부의 지식은 필요하지 않습니다.
- 테스트에 필요한 데이터는 GUI 또는 API를 사용하여 생성할 수 있습니다.
- 기대치는 브라우저 페이지와 API 응답에 대해서만 설정할 수 있습니다.
새로운 기능은 테스트 계획과 함께 제공되어야 합니다.
테스트 경로 | 테스트 엔진 | 참고 |
---|---|---|
qa/qa/specs/features/
| Capybara + RSpec + 사용자 정의 QA 프레임워크 | 테스트는 제품 범주에 해당하는 위치에 배치되어야 합니다. |
자세한 내용은 end-to-end 테스트를 참조하세요.
qa/spec
에는 QA 프레임워크의 유닛 테스트가 포함되어 있으며, 이는 응용프로그램의 유닛 테스트 또는 시스템 수준의 블랙박스 테스트(End-to-end 테스트)와 혼동해서는 안 됩니다.
스모크 테스트
스모크 테스트는 신속한 테스트로서 언제든지(특히 사전 배포 마이그레이션이 이후) 실행될 수 있습니다.
이러한 테스트는 UI에 대해 실행되며 기본 기능이 작동하는지 확인합니다.
자세한 정보는 스모크 테스트(Smoke Tests)를 참조하세요.
GitLab QA 오케스트레이터
GitLab QA 오케스트레이터는 주어진 GitLab Rails 버전에 대한 Docker 이미지를 빌드하고, 그 위에서 end-to-end 테스트(Capybara를 사용)를 실행하여 이러한 모든 부분이 서로 잘 통합되는지를 테스트할 수 있는 도구입니다.
자세한 사항은 GitLab QA 오케스트레이터 README에서 확인하세요.
EE(Enterprise Edition) 특정 테스트
EE(Enterprise Edition) 특정 테스트는 ee/spec
폴더 아래에 동일한 구성으로 따릅니다.
올바른 수준에서 테스트하는 방법?
인생의 다른 것들과 마찬가지로, 각 테스트 수준에서 무엇을 테스트해야 하는지를 결정하는 것은 트레이드오프입니다:
- 유닛 테스트는 보통 비용이 싸며, 코드가 올바르게 작동하는지 확신하기 위해 이러한 지하실과 같이 생각해야 합니다. 그러나 통합/시스템 테스트 없이 유닛 테스트만 실행한다면, 큰 그림을 놓치게 될 수 있습니다.
- 통합 테스트는 약간 비용이 더들지만, 과도하게 사용해서는 안 됩니다. 내부를 많이 스텁하는 통합 테스트보다는 종종 시스템 테스트가 더 나을 수 있습니다.
- 시스템 테스트는 유닛 테스트에 비해(특히 JavaScript 드라이버가 필요한 경우) 더 비용이 많이들며, 속도 섹션의 지침을 따를 필요가 있습니다.
다른 관점은 “테스트 비용”에 대해 생각해보는 것입니다. 이에 대한 기본 아이디어는 이 글에서 잘 설명되어 있습니다.
프론트엔드 관련 테스트
특히 UI, 애니메이션, 변칙적인 경우나 백엔드와 무관한 작은 동작을 테스트하는 경우 전체 응용프로그램을 실행하는 데 소요되는 시간보다 테스트하는 동작이 가치있는 경우, 프론트엔드 통합 테스트를 작성해야 합니다.