테스트 레벨

테스트 우선순위 삼각형

이 다이어그램은 사용하는 각 테스트 유형의 상대적 우선순위를 보여줍니다. e2e는 끝에서 끝까지를 의미합니다.

2019년 5월 1일을 기준으로, 각 레벨별 테스트 분포는 다음과 같습니다.

테스트 레벨 커뮤니티 에디션 엔터프라이즈 에디션 커뮤니티 + 엔터프라이즈 에디션
시스템 레벨의 블랙박스 테스트(끝에서 끝까지 또는 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 Rails 마이그레이션 테스트 가이드에서 자세히 설명됨.
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  

프론트엔드 단위 테스트

단위 테스트는 일반적으로 사용자가 직접 인지할 수 없는 기능을 테스트하는 가장 낮은 추상화 수준에 있습니다.

graph RL plain[일반 자바스크립트]; Vue[Vue 컴포넌트]; feature-flags[기능 플래그]; license-checks[라이선스 확인]; plain---Vuex; plain---GraphQL; Vue---plain; Vue---Vuex; Vue---GraphQL; browser---plain; browser---Vue; plain---백엔드; Vuex---백엔드; GraphQL---백엔드; Vue---백엔드; 백엔드---데이터베이스; 백엔드---기능 플래그; 백엔드---라이선스 확인; class plain tested; class Vuex tested; classDef node color:#909090,fill:#f0f0f0,stroke-width:2px,stroke:#909090 classDef label stroke-width:0; classDef tested color:#000000,fill:#a0c0ff,stroke:#6666ff,stroke-width:2px,stroke-dasharray: 5, 5; subgraph " " tested; mocked; class tested tested; end

단위 테스트 사용 시점

  • 내보낸 함수 및 클래스: 내보낸 모든 것은 제어할 수 없는 다양한 장소에서 재사용될 수 있습니다. 퍼블릭 인터페이스의 예상 동작을 테스트로 문서화해야 합니다.
  • Vuex 액션: 모든 Vuex 액션이 특정 컴포넌트에서 트리거됐는지와는 관계없이 일관된 방식으로 작동해야 합니다.
  • Vuex 뮤테이션: 복잡한 Vuex 뮤테이션의 경우, 다른 부분과 테스트를 분리하여 문제 해결을 단순화해야 합니다.

단위 테스트 하지 말아야 하는 시점

  • 내보내지 않는 함수 또는 클래스: 모듈에서 내보내지 않은 모든 것은 비공개 또는 구현 세부 정보로 간주될 수 있으며 테스트할 필요가 없습니다.
  • 상수: 상수의 값을 테스트하는 것은 복사하여 추가 노력을 들이면서 값이 올바른지에 대한 추가적인 확신을 가지는 데 도움이 되지 않습니다.
  • Vue 컴포넌트: 컴포넌트의 계산된 속성, 메서드 및 라이프사이클 훅은 컴포넌트 테스트에 의해 암시적으로 다뤄지므로 테스트할 필요가 없습니다. 자세한 내용은 공식 Vue 가이드를 참조하십시오.

유닛 테스트에서는 무엇을 가장하면 좋을까요

  • 테스트 대상 클래스의 상태: 테스트 대상 클래스의 상태를 직접 수정하는 대신 클래스의 메서드를 사용하는 것이 테스트 설정에서 부수 효과를 피할 수 있습니다.
  • 다른 내보낸 클래스: 각 클래스는 지수적으로 늘어난 테스트 시나리오를 방지하기 위해 격리된 상태에서 테스트되어야 합니다.
  • 단일 DOM 요소 (매개변수로 전달된 경우): 전체 페이지가 아니라 개별 DOM 요소에서만 작동하는 테스트의 경우, 이러한 요소를 생성하는 것이 전체 HTML fixture을 로드하는 것보다 더 저렴합니다.
  • 모든 서버 요청: 프론트엔드 단위 테스트를 실행할 때 백엔드에 연결할 수 없을 수 있으므로 모든 외부 요청은 가상화되어야 합니다.
  • 비동기 백그라운드 작업: 백그라운드 작업은 중지되거나 대기할 수 없으므로 다음 테스트에서 계속 실행되어 부수 효과를 일으킵니다.

유닛 테스트에서 가장 하지 말아야 할 것들

  • 내보내지 않는 함수 또는 클래스: 내보내지 않는 모든 것은 해당 모듈에 대해 비공개로 간주될 수 있으며, 명시적으로 내보내는 클래스 및 함수를 통해 암묵적으로 테스트됩니다.
  • 테스트 대상 클래스의 메서드: 테스트 대상 클래스의 메서드를 모킹함으로써 실제 메서드가 아닌 모크를 테스트하게 됩니다.
  • 유틸리티 함수 (순수 함수 또는 매개변수만 수정하는 함수): 함수가 상태가 없어 부수 효과가 없는 경우에는 테스트에서 모킹할 필요가 없습니다.
  • 전체 HTML 페이지: 유닛 테스트에서 전체 페이지의 HTML을 로드하는 것은 테스트를 느리게 만들 수 있으므로 피해야 합니다.

프론트엔드 컴포넌트 테스트

컴포넌트 테스트는 사용자에게 외부 신호에 따라 인지할 수 있는 단일 컴포넌트의 상태를 확인합니다. 이 신호는 사용자 입력, 다른 컴포넌트에서 발생한 이벤트 또는 응용 프로그램 상태와 같은 외부 신호가 될 수 있습니다.

graph RL plain[Plain JavaScript]; Vue[Vue Components]; feature-flags[Feature flags]; license-checks[License Checks]; plain---Vuex; plain---GraphQL; Vue---plain; Vue---Vuex; Vue---GraphQL; browser---plain; browser---Vue; plain---backend; Vuex---backend; GraphQL---backend; Vue---backend; backend---database; backend---feature-flags; backend---license-checks; class Vue tested; classDef node color:#909090,fill:#f0f0f0,stroke-width:2px,stroke:#909090 classDef label stroke-width:0; classDef tested color:#000000,fill:#a0c0ff,stroke:#6666ff,stroke-width:2px,stroke-dasharray: 5, 5; subgraph " " tested; mocked; class tested tested; end

컴포넌트 테스트를 사용하는 시점

  • 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와 상호 작용하는 것과 유사합니다.

graph RL plain[Plain JavaScript]; Vue[Vue Components]; feature-flags[Feature flags]; license-checks[License Checks]; plain---Vuex; plain---GraphQL; Vue---plain; Vue---Vuex; Vue---GraphQL; browser---plain; browser---Vue; plain---backend; Vuex---backend; GraphQL---backend; Vue---backend; backend---database; backend---feature-flags; backend---license-checks; class plain tested; class Vue tested; class Vuex tested; class GraphQL tested; class browser tested; linkStyle 0,1,2,3,4,5,6 stroke:#6666ff,stroke-width:2px,stroke-dasharray: 5, 5; classDef node color:#909090,fill:#f0f0f0,stroke-width:2px,stroke:#909090 classDef label stroke-width:0; classDef tested color:#000000,fill:#a0c0ff,stroke:#6666ff,stroke-width:2px,stroke-dasharray: 5, 5; subgraph " " tested; mocked; class tested tested; end

통합 테스트를 사용하는 시기

  • 페이지 번들 (app/assets/javascripts/pages/index.js 파일): 페이지 번들을 테스트하여 해당 프런트엔드 구성 요소가 잘 통합되는지 확인합니다.
  • 페이지 번들 외 Vue 애플리케이션: Vue 애플리케이션 전체를 테스트하여 해당 프런트엔드 구성 요소가 잘 통합되는지 확인합니다.

통합 테스트에서 모의할 대상

  • HAML 뷰 (대신 픽스처 사용): HAML 뷰를 렌더링하는 것은 실행 중인 데이터베이스를 포함한 Rails 환경이 필요하므로, 프런트엔드 테스트에서 신뢰할 수 없습니다.
  • 모든 서버 요청: 유사하게 단위 및 구성 요소 테스트와 마찬가지로 구성 요소 테스트를 실행할 때, 백엔드에 연결할 수 없을 수 있으므로 모든 외부 요청이 모의되어야 합니다.
  • 페이지에서 인지할 수 없는 비동기 백그라운드 작업: 페이지에 영향을 주는 백그라운드 작업은 이 수준에서 테스트해야 합니다. 다른 모든 백그라운드 작업은 중단되거나 기다릴 수 없으므로, 다음 테스트에서 계속 실행되어 부작용을 일으킵니다.

통합 테스트에서 모의하지 말아야 할 대상

  • DOM: 실제 DOM에서 테스트하는 것은 구성 요소가 의도한 환경에서 작동하는지 확인합니다. DOM 테스트의 일부는 크로스 브라우저 테스트로 위임됩니다.
  • 구성 요소의 속성 또는 상태: 이 수준에서 모든 테스트는 사용자가 수행할 수 있는 조치만 수행할 수 있습니다. 예를 들어: 구성 요소의 상태를 변경하려면 클릭 이벤트가 발생해야 합니다.
  • Vuex 저장소: 페이지의 프런트엔드 코드를 테스트할 때, Vue 구성 요소와 Vuex 저장소 간의 상호 작용도 포함됩니다.

컨트롤러 테스트 개요

GitLab은 컨트롤러 스펙에서 요청 스펙으로 전환하고 있습니다.

이상적으로 컨트롤러는 가늘어야 합니다. 그러나 그렇지 않을 때는 컨트롤러 테스트 대신 시스템 또는 기능 테스트를 작성하는 것이 허용됩니다. 일반적으로 두꺼운 컨트롤러를 테스트하는 것은 다음과 같은 많은 스텁이 필요합니다:

controller.instance_variable_set(:@user, user)

그리고 Rails 5에서 사용되지 않는 메서드를 사용합니다.

시스템 수준의 화이트박스 테스트(이전에는 시스템/기능 테스트로 알려짐)

공식적인 정의:

이러한 유형의 테스트는 GitLab Rails 애플리케이션(예: gitlab-foss/gitlab)이 브라우저 관점에서 예상대로 작동하는지 보장합니다.

주의할 점:

  • 응용프로그램의 내부에 대한 이해가 여전히 필요합니다.
  • 테스트에 필요한 데이터는 일반적으로 RSpec 팩토리를 사용하여 직접 생성됩니다.
  • 기대치는 데이터베이스나 객체 상태에 종종 설정됩니다.

이러한 테스트는 다음 경우에만 사용해야 합니다:

  • 테스트되는 기능/구성 요소가 작을 때
  • 객체/데이터베이스의 내부 상태를 테스트 해야할 때
  • 더 낮은 수준에서 테스트할 수 없는 경우

예를 들어, 주어진 페이지의 탐색 경로를 테스트하기 위해 시스템 테스트를 작성하는 것은 의미가 있습니다. 페이지 번들이나 컨트롤러 수준에서 테스트할 수 없는 작은 구성 요소이기 때문입니다.

행복한 경로만 테스트하되, 더 나은 테스트 환경에서 발견할 수 없는 재귀적인 경우에 대비하여 최저 수준에서 재귀적인 테스트를 추가하십시오(예: 재귀적인 경우가 발견되면, 가능한 한 최저 수준에서 재귀적 테스트가 추가되어야 합니다).

테스트 경로 테스팅 엔진 참고
spec/features/ Capybara + RSpec 테스트에 :js 메타데이터가 포함되어 있는 경우, 브라우저 드라이버는 Selenium를 사용하고, 그렇지 않은 경우 RackTest를 사용합니다.

프런트엔드 기능 테스트

프런트엔드 통합 테스트와는 달리, 특징 테스트는 픽스처 대신에 실제 백엔드에 요청을 보냅니다. 또한, 이로 인해 데이터베이스 쿼리가 실행되므로 이 범주는 훨씬 느립니다.

참고:

graph RL plain[Plain JavaScript]; Vue[Vue Components]; feature-flags[Feature flags]; license-checks[License Checks]; plain---Vuex; plain---GraphQL; Vue---plain; Vue---Vuex; Vue---GraphQL; browser---plain; browser---Vue; plain---backend; Vuex---backend; GraphQL---backend; Vue---backend; backend---database; backend---feature-flags; backend---license-checks; class backend tested; class plain tested; class Vue tested; class Vuex tested; class GraphQL tested; class browser tested; linkStyle 0,1,2,3,4,5,6,7,8,9,10 stroke:#6666ff,stroke-width:2px,stroke-dasharray: 5, 5; classDef node color:#909090,fill:#f0f0f0,stroke-width:2px,stroke:#909090 classDef label stroke-width:0; classDef tested color:#000000,fill:#a0c0ff,stroke:#6666ff,stroke-width:2px,stroke-dasharray: 5, 5; subgraph " " tested; mocked; class tested tested; mocked mocked; class label mocked; end
#### 특징 테스트를 사용하는 시기

- 백엔드가 필요하고 픽스처를 사용하여 테스트할 수 없는 사용 사례
- 페이지 번들의 일부가 아니지만 전역적으로 정의된 동작

#### 관련된 참고 사항

테스트에 전체 환경이 로드되도록 `:js` 플래그가 테스트에 추가됩니다:

```ruby
scenario '성공적으로', :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')

시스템 테스트를 작성하지 않는 것을 검토해보십시오

낮은 수준의 구성요소가 잘 작동하는 것에 확신이 있다면(충분한 단위 및 통합 테스트가 있다면), 시스템 테스트 수준에서 그들의 철저한 테스트를 중복해서 수행할 필요가 없습니다.

테스트를 추가하는 것은 매우 쉽지만, 테스트를 제거하거나 향상하는 것은 훨씬 어려우므로, 느리고 중복된 테스트를 너무 많이 도입하지 않도록 주의해야 합니다.

이러한 모범 사례를 따르는 이유는 다음과 같습니다:

  • 시스템 테스트는 전체 응용 프로그램 스택을 headless 브라우저에서 구동하므로 실행이 느리며, JS 드라이버를 통합할 경우 더욱 느립니다.
  • 시스템 테스트가 JavaScript 드라이버와 함께 실행될 때 테스트는 응용 프로그램과 다른 스레드에서 실행됩니다. 이는 데이터베이스 연결을 공유하지 않으며 실행 중인 응용 프로그램이 데이터를 보려면 테스트에서 트랜잭션을 커밋해야 합니다(그 반대도 마찬가지입니다). 이 경우에는 다른 종류의 테스트에 사용되는 더 빠른 전략인 트랜잭션 롤백 대신 각 spec 이후에 데이터베이스를 중단해야 합니다. 그러나 이것은 트랜잭션보다 느리기 때문에 필요할 때에만 중단을 사용하고자 합니다.

시스템 수준의 블랙박스 테스트, 즉 엔드 투 엔드 테스트

공식적인 정의:

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 드라이버가 필요한 경우 더욱 그렇습니다. 속도 섹션의 지침을 준수하십시오.

테스트의 “비용”에 대해 생각하는 다른 방법은 이 기사에 잘 설명되어 있으며, 기본 아이디어는 테스트의 비용이 다음을 포함한다는 것입니다:

  • 테스트 작성에 걸리는 시간
  • 테스트 스위트 실행 시마다 실행하는 시간
  • 테스트 이해에 걸리는 시간
  • 테스트가 실패할 경우 그것을 고치는 데 걸리는 시간 및 기반이 오류가 아닌 경우 코드를 변경하여 코드를 테스트할 수 있는 시간
  • 아마도, 코드를 테스트할 수 있도록 코드를 변경하는 데 걸리는 시간.

프론트엔드 관련 테스트

전체 응용 프로그램을 실행하는 데 들이는 시간에 비해 테스트하는 행위가 가치있지 않은 경우, 즉 스타일링, 애니메이션, 경계 조건 또는 백엔드를 포함하지 않는 작은 작업을 테스트하는 경우 프론트엔드 통합 테스트를 사용하여 통합 테스트를 작성해야 합니다.


테스트 문서로 돌아가기