테스트 수준

테스트 우선 순위 삼각형

이 다이어그램은 사용하는 각 테스트 유형의 상대적인 우선 순위를 보여줍니다. e2e는 end-to-end(종단간)을 나타냅니다.

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[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 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 가이드라인을 참조하세요.

유닛 테스트에서 모의(mock)해야 할 대상

  • 테스트 대상 클래스의 상태: 테스트 대상 클래스의 상태를 직접 수정하여 테스트 설정에서 부작용이 발생하는 것을 피해야 합니다.

  • 다른 내보낸 클래스: 모든 클래스는 테스트시에 지수적으로 늘어나는 테스트 시나리오를 방지하기 위해 독립적으로 테스트해야 합니다.

  • 단일 DOM 요소가 매개변수로 전달된 경우: 전체 페이지가 아닌 단일 DOM 요소에만 작용하는 테스트를 위해 전체 HTML Fixture를 로드하는 것보다 이러한 요소를 생성하는 것이 더 저렴합니다.

  • 모든 서버 요청: 프런트엔드 단위 테스트를 실행할 때 백엔드에 도달할 수 없을 수 있으므로 모든 외부 요청은 모의화해야 합니다.

  • 비동기 백그라운드 작업: 백그라운드 작업은 중지되거나 대기할 수 없으므로 다음 테스트에서 계속 실행되어 부작용을 일으킵니다.

유닛 테스트에서 모의(mock)해서는 안 되는 대상

  • 내보내지 않은 함수 또는 클래스: 내보내지 않는 모든 것은 모듈 내부용으로 간주되며 내보낸 클래스와 함수를 통해 암시적으로 테스트됩니다.

  • 테스트 대상 클래스의 메소드: 테스트 대상 클래스의 메소드를 모의화하면 모의(mock)가 테스트되고 실제 메소드가 테스트되지 않습니다.

  • 유틸리티 함수 (순수 함수 또는 매개변수만 수정하는 함수): 상태가 없으므로 부작용이 없는 함수는 테스트에서 모의(mock)화하지 않아도 안전합니다.

  • 전체 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 템플릿에는 마크업만 포함되어 있고 프런트엔드 측 로직이 없으므로 완전한 컴포넌트가 아닙니다.

컴포넌트 테스트에서 모의(mock)해야 하는 대상

  • 부작용: 외부 상태를 변경할 수 있는 모든 것 (예: 네트워크 요청)은 모의화해야 합니다.

  • 자식 컴포넌트: 각 컴포넌트는 개별적으로 테스트되므로 자식 컴포넌트가 모의화됩니다. 또한 shallowMount()를 참조하세요.

컴포넌트 테스트에서 모의(mock)하지 말아야 하는 대상

  • 테스트 대상 컴포넌트의 메소드 또는 계산된 속성: 테스트 대상 컴포넌트의 일부를 모의화하면 모의(mock)가 테스트되고 실제 컴포넌트가 테스트되지 않습니다.

  • Vuex: 부서지기 쉽고 잘못된 양성 결과를 피하기 위해 Vuex를 모의화하지 마세요. Mutation을 사용하여 Vuex 상태를 적절하게 설정하세요. Vuex 액션을 모의(mock)화하지 마세요.

통합 테스트

Formal definition: 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은 컨트롤러 스펙에서 요청 스펙으로 전환하고 있습니다.

이상적으로는 컨트롤러가 가늠성이어야 합니다. 그러나 그렇지 않은 경우, 컨트롤러 테스트 대신 시스템 또는 기능 테스트를 JavaScript 없이 작성하는 것이 허용됩니다. 일반적으로 뚱뚱한 컨트롤러를 테스트하는 경우 다음과 같은 많은 스텁이 필요합니다.

controller.instance_variable_set(:@user, user)

그리고 Rails 5에서 폐기된 메서드를 사용합니다. ## 시스템 수준에서의 화이트박스 테스트(이전에는 시스템/기능 테스트로 알려짐)

Formal definitions:

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

애플리케이션의 내부에 대한 이해가 여전히 필요하며, 테스트에 필요한 데이터는 일반적으로 직접적으로 RSpec 팩토리를 사용하여 생성합니다. 테스트에서 기대 사항은 데이터베이스나 객체 상태에 대해 자주 설정됩니다.

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

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

예를 들어, 특정 페이지의 이동 경로를 테스트하려면, 시스템 테스트를 작성하는 것이 의미가 있습니다. 페이지 이동 경로는 작은 구성 요소이며, 더 낮은 수준에서 더 좋은 테스트로 감지할 수 없기 때문입니다.

행복한 경우에만 테스트를 하지만, 더 나은 테스트로는 해당 문제를 해결할 수 없는 경우에만 경계 회귀 테스트 케이스를 추가하는 것이 중요합니다(예: 낮은 수준에서 감지할 수 없는 회귀가 발견되는 경우, 회귀 테스트 케이스는 가능한 낮은 수준에서 추가되어야 합니다).

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

프론트엔드 기능 테스트

프론트엔드 통합 테스트와는 달리, 기능 테스트는 픽스처(fixture) 대신 실제 백엔드에 요청을 보냅니다. 이는 데이터베이스 쿼리가 실행되며 해당 카테고리가 상당히 느리다는 점을 의미합니다.

참고:

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 백엔드 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

기능 테스트를 사용하는 시기

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

관련 노트

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

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')

시스템 테스트 작성을 하지 않을 고려사항

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

테스트를 추가하는 것은 매우 쉽지만, 테스트를 제거하거나 개선하는 것은 훨씬 어렵기 때문에 (느리고 중복된) 테스트를 너무 많이 도입하지 않도록 주의해야 합니다.

이러한 최고의 실천을 따르는 이유는 다음과 같습니다:

  • 시스템 테스트는 전체 응용프로그램 스택을 헤드리스 브라우저에서 실행하기 때문에 실행이 느립니다. 특히 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 드라이버가 필요한 경우 더욱 그렇습니다. 속도 섹션의 지침을 준수해야 합니다.

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

  • 테스트를 작성하는 데 걸리는 시간
  • 스위트가 실행될 때마다 테스트를 실행하는 데 걸리는 시간
  • 테스트를 이해하는 데 걸리는 시간
  • 테스트가 손상되었을 때 소스 코드에 문제가 없는 경우 테스트를 수정하는 데 걸리는 시간
  • 아마도 코드를 테스트할 수 있도록 코드를 변경하는 데 걸리는 시간

프론트엔드 관련 테스트

전체 응용 프로그램을 실행하는 데 들이는 시간보다 테스트하는 행동이 스타일링, 애니메이션, 경계 상황 또는 백엔드를 포함하지 않는 작은 작업과 같은 경우 등 일부 상황에서는 작성해야 하는 시간이 가치가 있는 테스트가 있습니다. 프론트엔드 통합 테스트를 사용하여 통합 테스트를 작성해야 합니다.


테스트 문서로 돌아가기