테스트 레벨

테스트 우선순위 삼각형

이 다이어그램은 사용하는 각 테스트 유형의 상대적인 우선 순위를 보여줍니다. e2e는 end-to-end의 약자입니다.

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 레코드가 필요하지 않습니다. 데이터베이스 레코드가 필요하지 않은 클래스는 최대한 stubs/doubles을 사용해야 합니다.

코드 경로 테스트 경로 테스팅 엔진 비고
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[Plain JavaScript]; 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[피처 플래그s]; 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[피처 플래그s]; 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은 컨트롤러 스펙에서 요청 스펙으로 전환하고 있습니다.

이상적으로는 컨트롤러를 가늠할 필요가 없습니다. 그러나 그렇지 않은 경우, 컨트롤러 테스트 대신 시스템 또는 기능 테스트를 JavaScript 없이 작성하는 것이 허용됩니다. 뚱뚱한 컨트롤러를 테스트하는 것은 다음과 같은 많은 스텁화를 포함합니다. ruby controller.instance_variable_set(:@user, user)

그리고 Rails 5에서 폐기된 메서드를 사용합니다.

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

공식적인 정의:

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

다음을 유의하세요.

  • 애플리케이션의 내부에 대한 지식이 여전히 필요합니다.
  • 테스트에 필요한 데이터는 보통 직접 RSpec 팩토리를 사용하여 생성됩니다.
  • 기대치는 데이터베이스 또는 객체 상태에 종종 설정됩니다.

이러한 테스트는 다음과 같은 경우에만 사용해야 합니다.

  • 테스트하는 기능/컴포넌트가 작은 경우
  • 객체/데이터베이스의 내부 상태를 테스트해야 하는 경우
  • 더 낮은 수준에서 테스트할 수 없는 경우

예를 들어, 주어진 페이지에서의 패스 크럼브를 테스트하는 경우 시스템 테스트를 작성하는 것이 의미가 있습니다. 왜냐하면 이 작은 구성요소는 유닛 또는 컨트롤러 수준에서 테스트할 수 없기 때문입니다.

행복한 경로만 테스트하십시오. 그러나 더 나은 테스트 수준에서 감지할 수 없는 경우에는 재현되지 않는 리그레션 사례에 대한 테스트 케이스를 추가하세요 (예: 리그레션을 찾았을 때는 실제로는 더 낮은 수준에서 리그레션 테스트를 추가해야 합니다).

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

프런트엔드 기능 테스트

프런트엔드 통합 테스트와 대조적으로, 기능 테스트는 픽스처를 사용하는 대신 실제 백엔드에 대한 요청을 보냅니다. 이는 또한 데이터베이스 쿼리가 실행된다는 것을 의미하며, 이는 이 범주를 상당히 느리게 만듭니다.

note
graph RL plain[Plain JavaScript]; Vue[Vue Components]; feature-flags[피처 플래그s]; 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; end

기능 테스트 사용 시기

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

관련 노트

각 테스트의 단계는 (capybara methods)를 사용하여 작성됩니다.

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

시나리오 '성공적으로', :js do
  sign_in(create(:admin))
end

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 드라이버와 실행될 경우, 테스트는 응용 프로그램과 다른 스레드에서 실행됩니다. 이는 데이터베이스 연결을 공유하지 않고 테스트가 트랜잭션을 커밋해야 한다는 것을 의미합니다. 이 경우 다른 종류의 테스트에 사용되는 더 빠른 전략인 트랜잭션 롤백 대신 각 사양 후에 데이터베이스를 잘라야 합니다. 이는 트랜잭션보다 느리기 때문에 필요할 때만 잘라야 합니다.

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

공식 정의:

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-specific 테스트

EE-specific 테스트는 동일한 구성을 따르지만 ee/spec 폴더 아래에 있습니다.

올바른 수준에서 테스트하는 방법

생활의 많은 것들과 마찬가지로, 테스트의 각 수준에서 무엇을 테스트할지 결정하는 것은 상충관계입니다.

  • 단위 테스트는 보통 저렴하며, 코드가 올바르게 작동하는 데 확신을 갖기 위해 필요합니다. 그러나 통합 / 시스템 테스트를 실행하지 않고 단위 테스트만 실행하면 / 픽처를 (놓치게) 될 수 있습니다.
  • 통합 테스트는 조금 더 비싸지만, 그것을 과용해서는 안 됩니다. 시스템 테스트가 많은 내부를 털어내는 통합 테스트보다 시스템 테스트가 종종 더 좋습니다.
  • 시스템 테스트는 (단위 테스트에 비해) 비싸며, 특히 JavaScript 드라이버가 필요한 경우 더 그렇습니다. 속도 섹션의 지침을 따르십시오.

다른 관점으로 볼 수 있는 방법은 “테스트의 비용”에 대해 생각하는 것입니다. 이것은 이 기사에서 잘 설명되어 있으며, 기본적인 아이디어는 테스트의 비용에는 다음이 포함됩니다.

  • 테스트를 작성하는 데 걸리는 시간
  • 스위트가 실행될 때마다 테스트를 실행하는 데 걸리는 시간
  • 테스트를 이해하는 데 걸리는 시간
  • 테스트가 실패하면 테스트를 수정하는 데 걸리는 시간과 기본 코드가 잘못되었다면 코드를 수정하는 데 걸리는 시간
  • 아마도 코드를 테스트할 수 있도록 코드를 변경하는 데 걸리는 시간.

프론트엔드 관련 테스트

전체 애플리케이션을 실행하는 데 드는 시간보다 테스트하는 동작이 가치가 없는 경우가 있습니다. 예를 들어 스타일링, 애니메이션, 엣지 케이스 또는 백엔드와 관련이 없는 작은 동작을 테스트하는 경우 프론트엔드 통합 테스트를 작성해야 합니다.


테스트 문서로 돌아가기