Skip to content

테스트

테스트 종류와 목적

  • 우리는 테스트를 이야기할 때 주로 다음과 같은 다양한 것을 포함하여 말한다.
종류 목적 규칙
단위 테스트 클래스 또는 함수 단위로 동작이 예상되로 되는지 검증 제한된 Mocking
기능 테스트 사용자 관점에 기대하는 대로 동작하는지 검증
또는 잘못된 작업 요청에 적절히 반응하것도 포함
Endpoint 커버리지
미친 원숭이
통합 테스트 다른 서비스나 리소스와 제대로 연동되는지 검증 스테이징 데이터
부하 테스트 성능 측정 분산 서비스 모니터링
엔드 투 엔드 테스트 전체 시스템 동작 검증 테스트 자동화

제한된 Mocking

단위 테스트는 DB나 다른 리소스가 연결되지 않은 환경에서 수행된다. 따라서 이러한 리소스를 참조하는 라이브러리는 테스트를 위해 동작을 흉내내는 Mocking을 적용해야 한다. Mocking의 단점은 과다하게 사용될 경우에 의존 라이브러리 최신 상태를 반영하지 않는다는 점이다. 이렇게 작성된 테스트는 운영 환경에서 실패를 미리 예상할 수 없도록 만든다.

제한된 Mocking은 단위 테스트 구현시 Mocking을 일부 영역에 제한적으로 적용해야 함을 의미한다.

Tip

단위 테스트를 세밀하고 촘촘하게 하려고 하지 마라. 그럴 시간에 더 정확한 코드를 작성하라. 단위 테스트 양을 늘리면 불안감을 덜 수는 있겠지만 배포해야 할 코드는 그대로라는 점을 명심하라.

  • I/O 발생 : 네트워크 소켓이나 파일, DB와 같은 리소스를 참조해서 테스트를 수행해야 할 때 단위 테스트 목적으로 Mocking을 사용할 수 있다.

  • CPU 점유 : 계산량이 많은 함수나 클래스를 테스트해야 할 때도 있을 것이다. 이런 함수를 테스트하면 테스트 자체가 느려진다. 지연되는 테스트를 감안할 때 Mocking 사용이 제한적으로 허용된다.

  • 특이 상황 : 여러 장애 상황에서 코드를 검증할 필요가 있다. 이를테면 DB가 연결은 되지만 쿼리를 실행할 때 바빠서 처리할 수 없다고 응답하는 경우다.

다음은 주요 Mocking 프레임워크다.

  • requests-mock : REST API 호출과 같은 네트워크 요청을 모방한다. 데코레이터를 지원한다.
  • unittest::mock: 이 클래스는 Mocking을 위한 다양한 데코레이터를 지원한다.
  • jest : Javascript 테스트 자동화 도구다. 내부에 모킹 함수를 제공한다.
  • @testing-library: DOM 테스트를 위한 도구로 많은 UI 프레임워크를 지원한다.
  • monkeypatch : pytest에 내장된 mocking 모듈이다. 우리는 앞으로 이것을 주로 사용한다.

Endpoint 커버리지

기능이 정확히 동작하는지 검증하려면 어플리케이션이 제공하는 API나 Endpoint를 호출해서 원하는 결과가 나오는지 확인해야 한다. 이를 위해 Endpoint를 제공하는 어플리케이션을 기동해야 한다.

기능 테스트에서 Endpoint 테스트 커버리지를 100%에 가깝게 유지해야 한다. 이렇게 정책을 만든 이유는 단위 테스트도 아니며 제대로된 코드를 제공하기 위함이다.

Note

그러나 기능 테스트에서 커버리지가 100%라고 해서 충분하다고 볼 수는 없다. 필요 조건일 뿐이다.

이때 주로 어플리케이션 구현에 사용된 프레임워크 전용 테스트 클라이언트를 사용한다.

  • WebTest : WSGI 기반 웹 프레임워크에서 사용할 수 있다.

미친 원숭이

기능 테스트에서 잊지 말아야 할 것이 있다. 실패를 테스트해서 잘못된 동작을 하지 않는지 검증해야 한다.

  • 퍼징 테스트 : 고의로 이상한 데이터를 보내서 어플리케이션을 실패하게 한다.
  • 침투 테스트 : 보안 취약점을 이용해 어플리케이션을 잘못 작동하도록 만든다.

스테이징 데이터

제한된 환경에서 실행하는 테스트는 DB나 외부 리소스는 테스트 의도에 따라 동작한다고 *가정*한다. 실제 환경에서 어플리케이션을 검증해서 정상적으로 동작할 것이라는 것을 보장하려면 스테이징 환경이 필요하다.

개발 환경에서 실제 환경과 유사하게 갖춘후 테스트를 해볼 수는 있을 것이다. 그러나 개발용 데이터와 구별되는 실제와 유사한 테스트용 데이터를 제공할 수 있는 환경이어야 한다.

개발 환경에서 성공한 검증이 실제 환경에서 잘못 작동하는 경우는 경험상 대부분 테스트 데이터 구축에 실패한 경우다. ORM 도구를 사용하는 것은 이러한 위험으로부터 일정 부분 어플리케이션을 보호할 수 있는 방법이지만 완벽하지 않다.

  • alembic: DB 마이그레이션을 위한 도구다. 버전을 관리한다.

분산 서비스 모니터링

어플리케이션은 항상 다중 노드로 분산해서 배포될 수 있어야 한다. 부하 테스트는 어플리케이션을 스케일아웃할 때 얼마나 늘릴 수 있는지 결정할 수 있는 도구일 뿐이다.

사용자 수를 10,50,100 과 같이 단계적으로 늘려나가면서 초기 구축 환경에서 어느 정도 사용자를 수용할 수 있는지 확인할 수 있어야 한다.

부하 테스트를 위해서는 어플리케이션에 부하를 주는 도구가 필요하다. 대부분 Endpoint를 호출할 때 동시사용자수를 변경해가면서 테스트할 수 있다.

  • Apache Bench: Apache 웹서버에 통합된 도구다. 설치와 사용이 간단하다.
  • nGrinder: 네이버에서 만든 도구다. Java와 Tomcat이 필요하다.
  • Hey: golang으로 만들어졌고 golang 실행환경이 필요하다. http2 프로토콜도 지원한다.
  • JMeter: Java 진영에서 꾸진히 사용되고 있는 도구다.
  • locust: 여러 머신에서 발생시키는 대규모 부하 테스트를 지원한다.

또한 부하 발생시 서비스에서 몇가지 지표를 확인할 수 있는 모니터링 도구가 필요하다. 여러 인스턴스에 분산되어 있는 서비스를 모니터링하기 위해서는 추가적인 엔지니어링이 필요하다.

로그 중앙 집중

Graylog가 거의 완벽한 기능을 제공하지만 설치가 까다롭고 ElasticSearch와 MongoDB가 통합된 솔루션이어서 도입에 의사결정이 필요하다.

단순 에러로그 관리로는 Sentry를 검토해볼 필요가 있다.

성능 지표 수집

Graylog로도 성능 지표 수집이 가능하다. graypy와 같은 패키지를 활용해야 한다.

  • Prometheus+Grafana : 확장성 한계를 제외하고 가장 괜찮은 조합이다.
  • StatD+Graphite: 바로 사용 가능한 간단한 조합이다.

테스트 자동화

모든 테스트를 자동화할 수는 없더라도 필수적인 테스트를 자동화하는 방법들은 고려를 해야 한다. 개발 환경에서 테스트 자동화와 구별해서 CI 환경에서 테스트 자동화라고 부르기도 한다. 두가지 모두 지속적인 통합(Continuous Integration - CI)를 실천하기 위한 방안 중 하나인 "Make Your Build Self-Testing" 개념에서 나온 것이다.

Note

개발환경에서 테스트 자동화 : 개발자가 코드에 대해 작성한 단위 테스트 클래스들을 자동으로 테스트 하도록 하고, 매일(일정한 주기로) 지속적으로 테스트를 수행하여 그 결과를 확인함으로써 지속적인 통합의 이점을 극대화시키고자 하는 목적을 지님

테스트 자동화는 Build-Test-Inspection-Deploy-Report의 순환적 작업내에서 보장해야 한다. 아래는 지속적인 통합에 대한 개념도다.

사용자 관점에서 실제 사용하는 UI를 렌더링하고 활용할 수 있는 도구로는 Selenium이 있다. 이를 테스트 자동화 체인에 포함시켜야 한다.

아래는 파이썬으로 작성된 엔드 투 엔드 테스트 자동화 예제다.

# Selenium 사이트에서 제공하는 예제임
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support.expected_conditions import presence_of_element_located

#This example requires Selenium WebDriver 3.13 or newer
with webdriver.Firefox() as driver:
    wait = WebDriverWait(driver, 10)
    driver.get("https://google.com/ncr")
    driver.find_element(By.NAME, "q").send_keys("cheese" + Keys.RETURN)
    first_result = wait.until(presence_of_element_located(By.CSS_SELECTOR, "h3>div"))
    print(first_result.get_attribute("textContent"))

파이썬에서 테스트 자동화로 잘 알려진 도구로 tox가 있다. 특정 파이썬 버전을 위한 가상환경을 만들고 테스트를 실행할 수 있도록 해준다. toxpipenv를 활용해 테스트를 자동화하면 편리하다.

알아 두면 좋은 것들

테스트와 관련해서 중요하지 않지만 알아두면 좋은 내용들이 있다.

설계상의 인지 오류

프로그램을 구현할 때 이러저러한 가정으로부터 시작한다. 가정이란 **현실세계를 모델로 단순화 시키기 위한 것**이다. 예를 들면 사번을 Primary Key로 한다는 것은 현실 세계에서 사번이 바뀌지 않을 것이라는 가정에서 출발한다.일반적으로 사번이 바뀔 것을 대비해 데이터 모델 설계하려고 하지 않는다. 하지만 현실 세계에서는 재입사자가 존재한다. 시스템은 단순성을 위해서 재입사자는 다른 사번으로 관리한다고 정의한다.

모델이 현실 세계에 적용될 때 허용 가능한 범위를 **제약조건**이라고 한다. 사번은 최초 12자리로 설계하지만 현실 세계에서는 향후 얼마든지 바뀔 수 있음을 알고 있다. 일반사용자/관리자 별 시나리오는 특정한 사용자를 전제로 한 프로세스 모델이다. 하나의 유스케이스를 작성할 때 가정에 기반해서 제약조건을 면밀히 검토해야 한다. "로그인한 사용자에 대하여.." 만 있으면 되는 것이 아니라 "로그인한 사용자가 아닌 경우에는..." 도 있어야 한다.

*설계상의 인지 오류*는 제약조건에 불과한 것을 가정으로 생각하는 것이다. 테스트 시나리오는 가정을 토대로 해야 한다. 제약조건 내에서만 테스트하면 실패를 테스트한다는 원칙을 무시하게 된다.

관련 증상

  • 화면별 하나씩의 서비스 레이어와 데이터 레이어 컴포넌트를 둔다.
  • 솔직히 말하면 우리는 “비즈니스 프로세스를 모델링 하고 있지 않다.” – 단순 CRUD 작업 뿐이다.
  • 개발자들이 업무를 몰라서 시스템 상의 오류가 많다고 생각한다.
  • 요구사항이 빈번해서 시스템이 안정화되지 않는다는 말을 한다.

보안 테스트

주로 다음과 같은 보안 취약점을 테스트 한다.

  • SQLinjection
  • XSS
  • 파일업로드/다운로드
  • 디렉터리 인덱싱
  • 관리자 페이지 노출
  • 쿠기 변조 및 재사용
  • 부적절한 파라미터
  • 사용자측 스크립트 변조
  • 에러 페이지 노출
  • 중요 정보 노출
  • 백업 파일 노출
  • 기본 설치 페이지 노출
  • HTTPMethod 정보 노출

이외에도 ARP 스푸핑을 이용한 암호 변조 도구나 웹 프록시를 이용한 변조 도구들도 있다.

Warning

보안 테스트에 기재된 방법 자체가 이미 보안성을 위반하는 것이다. 따라서 보안 테스트를 연습한다고 임의로 도구를 사용할 때 보안성을 위반하게 되어 퇴사하게 될 수 있다.

성능 테스트

부하 테스트는 성능 테스트 범주에 들어간다. 성능 테스트를 다음과 같이 구분하기도 한다.

  • 부하 테스트(Load Test) : 일정 시간 동안 부하를 가하여 서버가 처리할 수 있는 최대 TPS와 각 기능의 응답시간 측정
  • 내구성 테스트(Endurance Test) : 긴 시간 동안 부하를 주어 시스템의 안정성을 점검
  • 스트레스 테스트(Stress Test): 최대 TPS 이상의 부하를 일시적으로 주어 시스템의 안정성을 점검
  • 스파이크 테스트(Spike Test): 순간적으로 사용자 수를 증가시키는 테스트

성능 테스트시 사용되는 지표는 필요한 것들만 있으면 된다.

  • 응답 시간 정보
  • 트랜잭션(요청) 성공 및 실패 건수
  • CPU 및 네트워크 사용량
  • 연계 리소스 처리 건수

성능 테스트 결과는 다음 내용을 포함하여야 한다.

  • 성능 테스트 개요
  • 테스트 진행 경과
  • 테스트 환경
  • 테스트 대상 및 시나리오
  • 테스트 결과
  • 종합 의견 및 권고 사항

댓글