테스트 수준
이 다이어그램은 우리가 사용하는 각 테스트 유형의 상대적 우선 순위를 보여줍니다. e2e
는 엔드 투 엔드(끝에서 끝까지)를 의미합니다.
2019-05-01 기준으로, 우리는 각 수준별 테스트의 다음과 같은 분포를 가지고 있습니다:
테스트 수준 | 커뮤니티 에디션 | 엔터프라이즈 에디션 | 커뮤니티 + 엔터프라이즈 에디션 |
---|---|---|---|
시스템 수준에서의 블랙박스 테스트(즉, 엔드 투 엔드 또는 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 레코드가 필요하지 않아야 합니다. 데이터베이스 레코드가 필요하지 않은 클래스는 가능한 한 스텁/더블을 사용해야 합니다.
코드 경로 | 테스트 경로 | 테스트 엔진 | 비고 |
---|---|---|---|
app/assets/javascripts/ |
spec/frontend/ |
Jest | 프론트엔드 테스트 가이드 섹션에서 자세한 내용을 보세요. |
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 | 테스트 레일즈 마이그레이션 가이드에서 자세한 내용을 보세요. |
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 |
프론트엔드 단위 테스트
단위 테스트는 가장 낮은 추상화 수준에 있으며, 일반적으로 사용자에 의해 직접 인식할 수 없는 기능성을 테스트합니다.
단위 테스트를 사용할 때
-
내보낸 함수 및 클래스: 내보낸 것은 다양한 장소에서 통제할 수 없는 방식으로 재사용될 수 있습니다. 공용 인터페이스의 예상 동작을 테스트를 통해 문서화해야 합니다.
-
Vuex 액션: 모든 Vuex 액션은 트리거되는 구성 요소와 독립적으로 일관된 방식으로 작동해야 합니다.
-
Vuex 변이: 복잡한 Vuex 변이의 경우, 문제 해결을 단순화하기 위해 테스트를 Vuex 스토어의 다른 부분과 분리해야 합니다.
단위 테스트를 사용하지 말아야 할 때
-
비내보낸 함수 또는 클래스: 모듈에서 내보내지 않은 것은 비공식적이거나 구현 세부사항으로 간주할 수 있으며 테스트할 필요가 없습니다.
-
상수: 상수의 값을 테스트하는 것은 그것을 복사하는 것을 의미하며, 이로 인해 추가적인 노력만 발생하고 값이 정확하다는 추가적인 확신을 제공하지 않습니다.
-
Vue 구성 요소: 계산된 속성, 메서드 및 생명주기 훅은 구성 요소의 구현 세부사항으로 간주될 수 있으며, 구성 요소 테스트에 의해 암묵적으로 포함되므로 테스트할 필요가 없습니다.
더 많은 정보는 공식 Vue 가이드라인을 참조하십시오.
단위 테스트에서 모의해야 할 것
-
테스트 중인 클래스의 상태: 테스트 중인 클래스의 상태를 직접 수정하는 것보다 클래스의 메서드를 사용하는 것이 테스트 설정에서의 부작용을 피하는 방법입니다.
-
다른 내보낸 클래스: 모든 클래스는 테스트 시나리오가 기하급수적으로 증가하는 것을 방지하기 위해 격리된 상태로 테스트되어야 합니다.
-
매개변수로 전달되는 단일 DOM 요소: 단일 DOM 요소에서만 작업하는 테스트의 경우, 전체 페이지가 아니라 이러한 요소를 만드는 것이 전체 HTML 픽스처를 로드하는 것보다 비용이 적습니다.
-
모든 서버 요청: 프론트엔드 단위 테스트를 실행할 때 백엔드에 접근할 수 없을 수 있으므로 모든 아웃고잉 요청은 모의해야 합니다.
-
비동기 백그라운드 작업: 백그라운드 작업은 중단하거나 기다릴 수 없으므로, 다음 테스트에서도 계속 실행되고 부작용을 일으킵니다.
단위 테스트에서 모의하지 말아야 할 것
-
비내보낸 함수 또는 클래스: 내보내지 않은 모든 것은 모듈에 비공식적이며, 내보낸 클래스와 함수로 암묵적으로 테스트됩니다.
-
테스트 중인 클래스의 메서드: 테스트 중인 클래스의 메서드를 모의하면, 모의된 메서드가 테스트되고 실제 메서드는 테스트되지 않습니다.
-
유틸리티 함수 (순수 함수 또는 매개변수만 수정하는 함수): 함수에 상태가 없기 때문에 부작용이 없다면 테스트에서 모의하지 않는 것이 안전합니다.
-
전체 HTML 페이지: 단위 테스트에서 전체 페이지의 HTML을 로드하는 것은 테스트 속도를 저하시킵니다.
프론트엔드 컴포넌트 테스트
컴포넌트 테스트는 사용자 입력, 다른 컴포넌트에서 발생하는 이벤트 또는 애플리케이션 상태와 같은 외부 신호에 따라 사용자가 인식할 수 있는 단일 컴포넌트의 상태를 다룹니다.
컴포넌트 테스트를 사용해야 할 때
- Vue 컴포넌트
컴포넌트 테스트를 사용하지 말아야 할 때
-
Vue 애플리케이션: Vue 애플리케이션에는 많은 컴포넌트가 포함될 수 있습니다.
이들을 컴포넌트 수준에서 테스트하려면 너무 많은 노력이 필요합니다.
따라서 프론트엔드 통합 수준에서 테스트됩니다. -
HAML 템플릿: HAML 템플릿은 마크업만 포함하고 프론트엔드 측 논리가 없습니다.
따라서 이들은 완전한 컴포넌트가 아닙니다.
컴포넌트 테스트에서 모킹해야 할 것
-
부작용: 외부 상태를 변경할 수 있는 모든 것(예: 네트워크 요청)은 모킹해야 합니다.
-
하위 컴포넌트: 각 컴포넌트는 개별적으로 테스트되므로 하위 컴포넌트는 모킹됩니다.
shallowMount()
참조하세요.
컴포넌트 테스트에서 모킹하지 말아야 할 것
-
테스트 중인 컴포넌트의 메서드 또는 계산 속성: 테스트 중인 컴포넌트의 일부를 모킹하면, 모킹된 부분이 테스트되고 실제 컴포넌트는 테스트되지 않습니다.
-
Vuex: 취약하고 잘못된 긍정 테스트를 피하기 위해 Vuex를 모킹하지 않도록 합니다.
뮤테이션을 사용하여 Vuex를 적절한 상태로 설정합니다.
Vuex 액션이 아닌 부작용을 모킹합니다.
통합 테스트
정식 정의: 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 애플리케이션(예: gitlab-foss
/gitlab
)이 브라우저 관점에서 예상대로 작동하는지 확인합니다.
다음 사항에 유의하세요:
- 애플리케이션의 내부 구조에 대한 지식이 여전히 필요합니다.
- 테스트에 필요한 데이터는 일반적으로 RSpec 팩토리를 사용하여 직접 생성됩니다.
- 기대치는 종종 데이터베이스 또는 객체 상태에 설정됩니다.
이러한 테스트는 다음과 같은 경우에만 사용해야 합니다:
- 테스트되는 기능/구성이 작을 때
- 객체/데이터베이스의 내부 상태를 테스트해야 할 때
- 더 낮은 수준에서 테스트할 수 없을 때
예를 들어, 주어진 페이지의 빵부스러기를 테스트하기 위해 시스템 테스트를 작성하는 것이 의미가 있습니다. 이는 작은 구성 요소이고, 단위 또는 컨트롤러 수준에서 테스트할 수 없기 때문입니다.
행복한 경로만 테스트하되, 더 낮은 수준에서 더 나은 테스트로 포착할 수 없었던 회귀에 대한 테스트 케이스를 추가하는 것을 잊지 마세요 (예: 회귀가 발견되면 가장 낮은 수준에서 회귀 테스트를 추가해야 합니다).
테스트 경로 | 테스트 엔진 | 비고 |
---|---|---|
spec/features/ |
Capybara + RSpec | 테스트에 :js 메타데이터가 포함되면 브라우저 드라이버는 Selenium을 사용하고, 그렇지 않으면 RackTest를 사용합니다. |
프론트엔드 기능 테스트
프론트엔드 통합 테스트와 달리, 기능 테스트는 픽스처 대신 실제 백엔드에 대한 요청을 보냅니다.
이것은 또한 데이터베이스 쿼리가 실행됨을 의미하며, 이 카테고리는 상당히 느려집니다.
참고하세요:
- RSpec 테스트 가이드라인.
- 테스트 모범 사례에서 시스템 / 기능 테스트.
기능 테스트를 언제 사용해야 하는가
- 백엔드가 필요한 경우이며, 픽스를 사용하여 테스트할 수 없는 경우.
- 페이지 번들에 포함되지 않지만 전역적으로 정의된 동작.
관련 메모
테스트가 전체 환경이 로드되었는지 확인하기 위해 :js
플래그가 추가됩니다:
scenario 'successfully', :js do
sign_in(create(:admin))
end
각 테스트의 단계는 (capybara 메서드)를 사용하여 작성됩니다.
XHR (XMLHttpRequest) 호출은 단계 사이에 wait_for_requests
를 사용해야 할 수 있습니다, 예:
find('.form-control').native.send_keys(:enter)
wait_for_requests
expect(page).not_to have_selector('.card')
시스템 테스트를 작성하지 않는 것을 고려하세요
저수준 구성 요소가 잘 작동한다고 확신하는 경우(그리고 충분한 단위 및 통합 테스트가 있는 경우 그래야 합니다), 시스템 테스트 수준에서 그들의 철저한 테스트를 중복할 필요가 없습니다.
테스트를 추가하는 것은 매우 쉽지만, 제거하거나 개선하는 것은 훨씬 더 어렵기 때문에, 너무 많은 (느리고 중복된) 테스트를 도입하지 않도록 주의해야 합니다.
이러한 모범 사례를 따라야 하는 이유는 다음과 같습니다:
- 시스템 테스트는 전체 애플리케이션 스택을 헤드리스 브라우저에서 실행하기 때문에 느리게 실행됩니다. JavaScript 드라이버가 통합될 때는 더 느려집니다.
- 시스템 테스트가 JavaScript 드라이버와 함께 실행될 때, 테스트는 애플리케이션과 다른 스레드에서 실행됩니다. 이는 데이터베이스 연결을 공유하지 않으며, 테스트는 실행 중인 애플리케이션이 데이터를 볼 수 있도록 거래를 커밋해야 합니다(그리고 그 반대도 마찬가지입니다). 이 경우 각 사양 후 데이터베이스를 잘라내야 하며, 다른 종류의 테스트에서 사용하는 더 빠른 전략인 거래를 롤백해야 합니다. 하지만 거래보다 느리므로 필요할 때만 잘라내기를 사용해야 합니다.
시스템 수준의 블랙 박스 테스트, 즉 엔드 투 엔드 테스트
정식 정의:
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 프레임워크 | 테스트는 해당 제품 카테고리에 배치해야 합니다. |
자세한 내용은 엔드 투 엔드 테스트를 참조하세요.
qa/spec
는 QA 프레임워크 자체의 단위 테스트를 포함하므로, 애플리케이션의 단위 테스트 또는 엔드 투 엔드 테스트와 혼동하지 않도록 유의하세요.
스모크 테스트
스모크 테스트는 언제든지 실행할 수 있는 빠른 테스트입니다(특히 배포 전 데이터 마이그레이션 후).
이 테스트는 UI에 대해 실행되며 기본 기능이 작동하는지 확인합니다.
더 많은 정보는 스모크 테스트를 참조하세요.
GitLab QA 오케스트레이터
GitLab QA 오케스트레이터는 주어진 버전의 GitLab Rails에 대한 Docker 이미지를 빌드하고 그에 대해 엔드 투 엔드 테스트(예: Capybara 사용)를 실행할 수 있도록 해주는 도구입니다.
자세한 내용은 GitLab QA 오케스트레이터 README를 참고하세요.
EE 전용 테스트
EE 전용 테스트는 동일한 조직을 따르지만 ee/spec
폴더 아래에 있습니다.
올바른 수준에서 테스트하는 방법은?
인생의 많은 것들처럼 각 테스트 수준에서 무엇을 테스트할지 결정하는 것은 절충입니다:
- 유닛 테스트는 일반적으로 저렴하며, 코드가 올바르게 작동한다고 확신하기 위해 집의 기초처럼 필요합니다. 그러나 통합/시스템 테스트 없이 유닛 테스트만 실행하면 놓칠 수 있습니다 큰 / 그림을 !
- 통합 테스트는 조금 더 비쌉니다. 하지만 남용하지 마세요. 시스템 테스트는 종종 많은 내부를 스텁하는 통합 테스트보다 낫습니다.
- 시스템 테스트는 비쌉니다(유닛 테스트와 비교할 때), JavaScript 드라이버가 필요한 경우 더욱 그렇습니다. 속도 섹션의 지침을 따르는 것을 잊지 마세요.
또 다른 방법은 “테스트의 비용”에 대해 생각하는 것입니다. 이는 이 기사에서 잘 설명되어 있으며, 기본 아이디어는 테스트의 비용에 다음이 포함된다는 것입니다:
- 테스트를 작성하는 데 걸리는 시간 -Suite가 실행될 때마다 테스트를 실행하는 데 걸리는 시간
- 테스트를 이해하는 데 걸리는 시간
- 테스트가 실패했을 때 테스트를 수정하는 데 걸리는 시간(기저 코드가 괜찮은 경우)
- 아마도, 코드를 테스트 가능하게 만들기 위해 코드를 변경하는 데 걸리는 시간.
프론트엔드 관련 테스트
테스트하고 있는 동작이 전체 애플리케이션을 실행할 때 소요되는 시간만큼의 가치가 없는 경우가 있습니다. 예를 들어, 스타일링, 애니메이션, 에지 케이스 또는 백엔드를 포함하지 않는 작은 작업을 테스트하는 경우, 프론트엔드 통합 테스트를 사용하여 통합 테스트를 작성해야 합니다.