테스트 계획의 요소 (Elements of a Test Plan)

테스트 계획의 요소 (Elements of a Test Plan)

테스트 계획은 JMeter가 동작하게 하는 모든 구성 요소의 틀이에요. 최소한의 테스트는 Test Plan, 스레드 그룹(Thread Group), 하나 이상의 샘플러(Sampler)로 구성돼요. 여기서는 스레드 그룹, 컨트롤러, 샘플러, 리스너, 타이머, 어서션, 구성 요소, 실행 순서와 스코핑(적용 범위) 규칙까지 테스트 계획을 이루는 각 부분을 살펴볼게요.

출처: Elements of a Test Plan — Apache JMeter 공식 문서

본문

3.0 테스트 계획 (Test Plan)

Test Plan 객체에는 "Functional Testing" 체크박스가 있어요. 이걸 선택하면 JMeter가 각 샘플에 대해 서버에서 반환된 데이터를 기록해요. 테스트 리스너에서 파일을 선택했다면 이 데이터가 파일로 쓰여지고요. 설정이 잘 됐는지, 서버가 기대한 결과를 돌려주는지 확인하려고 작게 돌릴 때 유용해요. 단, 그 결과 파일은 금방 엄청나게 커지고 JMeter 성능도 떨어져요. 스트레스 테스트를 할 때는 이 옵션을 꺼야 해요(기본적으로 꺼져 있어요). 데이터를 파일로 기록하지 않는다면 이 옵션은 차이가 없어요. 리스너의 Configuration 버튼으로 어떤 필드를 저장할지 정할 수도 있어요.

3.1 스레드 그룹 (Thread Group)

스레드 그룹 요소는 어떤 테스트 계획이든 시작점이 돼요. 모든 컨트롤러와 샘플러는 스레드 그룹 아래에 있어야 해요. 리스너 같은 다른 요소는 테스트 계획 바로 아래에 둘 수 있고, 그러면 모든 스레드 그룹에 적용돼요. 이름이 뜻하듯 스레드 그룹 요소는 JMeter가 테스트를 실행할 때 사용할 스레드 수를 제어해요. 스레드 그룹의 컨트롤로 다음을 설정할 수 있어요.

  • 스레드 수 (number of threads)
  • 램프업 시간 (ramp-up period)
  • 테스트 실행 횟수 (number of times to execute the test)

각 스레드는 테스트 계획 전체를 다른 테스트 스레드와 완전히 독립적으로 실행해요. 여러 스레드는 서버 애플리케이션에 대한 동시 연결을 시뮬레이션하는 데 사용돼요.

램프업 시간은 JMeter가 선택한 전체 스레드 수까지 "램프업"하는 데 걸리는 시간을 알려줘요. 10개의 스레드에 램프업 시간이 100초라면, JMeter는 10개 스레드를 모두 기동하는 데 100초가 걸려요. 각 스레드는 이전 스레드가 시작된 후 10(100/10)초 뒤에 시작돼요. 스레드 30개에 램프업 120초라면 각 연속 스레드는 4초씩 지연돼요.

램프업은 테스트 시작 시 작업 부하가 너무 커지지 않도록 충분히 길어야 하고, 첫 스레드가 끝나기 전에 마지막 스레드가 시작될 만큼(그렇게 되길 원하지 않는다면) 짧아야 해요. 램프업 = 스레드 수에서 시작해서 필요에 따라 위아래로 조정해보세요.

기본적으로 스레드 그룹은 요소를 한 번 루프하도록 설정돼요. 스레드 그룹은 스레드 수명(Thread lifetime)도 지정할 수 있어요. 스레드 그룹 패널 아래쪽 체크박스를 클릭하면 테스트 기간(duration)과 시작 지연(startup delay)을 입력할 수 있는 추가 필드가 활성화돼요. Duration(초)과 Startup Delay(초)를 설정해 각 스레드 그룹의 지속 시간과 몇 초 후 시작할지를 제어할 수 있어요. 테스트가 시작되면 JMeter는 스레드 그룹의 스레드를 시작하기 전에 Startup Delay(초)만큼 기다리고, 설정된 Duration(초) 시간 동안 실행해요.

3.2 컨트롤러 (Controllers)

JMeter에는 두 종류의 컨트롤러가 있어요: 샘플러(Samplers)와 논리 컨트롤러(Logical Controllers). 이 둘이 테스트 처리를 이끌어요.

  • 샘플러는 JMeter가 서버에 요청을 보내도록 해요. 예를 들어 HTTP 요청을 보내려면 HTTP Request 샘플러를 추가하면 돼요. 샘플러에 구성 요소(Configuration Element)를 하나 이상 추가해서 요청을 커스터마이즈할 수도 있어요.
  • 논리 컨트롤러는 JMeter가 언제 요청을 보낼지 결정하는 로직을 커스터마이즈하게 해줘요. 예를 들어 Interleave 논리 컨트롤러를 추가해서 두 HTTP Request 샘플러를 번갈아 실행할 수 있어요.

3.2.1 샘플러 (Samplers) — 샘플러는 JMeter가 서버에 요청을 보내고 응답을 기다리게 해요. 트리에 나타난 순서대로 처리되고, 컨트롤러로 샘플러의 반복 횟수를 바꿀 수 있어요. JMeter 샘플러에는 FTP Request, HTTP Request(SOAP 또는 REST 웹서비스에도 사용 가능), JDBC Request, Java object request, JMS request, JUnit Test request, LDAP Request, Mail request, OS Process request, TCP request가 있어요. 각 샘플러는 설정할 수 있는 여러 프로퍼티가 있고, 테스트 계획에 구성 요소를 하나 이상 추가해 더 커스터마이즈할 수 있어요. 같은 서버에 같은 유형의 요청(예: HTTP Request)을 여러 번 보낸다면 Defaults 구성 요소 사용을 고려해보세요. 각 컨트롤러에는 하나 이상의 Defaults 요소가 있어요. 요청 결과를 보거나 디스크에 저장하려면 테스트 계획에 리스너를 추가하는 걸 잊지 마세요. 요청 응답에 대해 기본적인 검증을 수행하고 싶다면 샘플러에 어서션(Assertion)을 추가해요. 예를 들어 웹 애플리케이션 스트레스 테스트에서 서버가 성공적인 "HTTP Response" 코드를 돌려줬어도 페이지에 오류가 있거나 섹션이 빠져 있을 수 있어요. 특정 HTML 태그, 흔한 오류 문자열 등을 확인하도록 어서션을 추가할 수 있고, JMeter는 이런 어서션을 정규표현식으로 만들 수 있게 해줘요.

3.2.2 논리 컨트롤러 (Logic Controllers) — 논리 컨트롤러는 JMeter가 요청을 보낼 때를 결정하는 로직을 커스터마이즈해요. 자식 요소에서 오는 요청의 순서를 바꾸거나, 요청 자체를 수정하거나, 요청을 반복하게 만들 수도 있어요. 논리 컨트롤러가 테스트 계획에 미치는 효과를 이해하기 위해 다음 테스트 트리를 생각해보세요.

Test Plan
Thread Group
  Once Only Controller
    Login Request (an HTTP Request)
  Load Search Page (HTTP Sampler)
  Interleave Controller
    Search "A" (HTTP Sampler)
    Search "B" (HTTP Sampler)
    HTTP default request (Configuration Element)
  HTTP default request (Configuration Element)
  Cookie Manager (Configuration Element)

이 테스트에서 로그인 요청은 첫 번째 통과 때만 실행돼요. 이후 반복에서는 건너뛰는데, 이는 Once Only Controller의 효과예요. 로그인 후 다음 샘플러가 검색 페이지를 로드해요(사용자가 로그인하고 검색 페이지로 가서 검색하는 웹 애플리케이션을 떠올려보세요). 이건 단순 요청이라 논리 컨트롤러를 거치지 않아요. 검색 페이지를 로드한 다음 검색을 하고 싶은데, 서로 다른 두 가지 검색을 하고 싶어요. 그러나 각 검색 사이에 검색 페이지를 다시 로드하고 싶어요. HTTP 요청 요소 4개(load search, search "A", load search, search "B")로 이걸 할 수 있겠지만, Interleave Controller를 쓰면 테스트를 통과할 때마다 자식 요청을 하나씩 넘겨줘요. 자식 요소의 순서를 유지하는데, 무작위로 넘기지 않고 자기 위치를 "기억"해요. 자식 요청 2개를 인터리브하는 건 과할 수 있지만, 8개나 20개가 될 수도 있으니 유용해요. Interleave Controller에 속한 HTTP Request Defaults를 주목해보세요. "Search A"와 "Search B"가 같은 PATH 정보를 공유한다고 해볼게요(HTTP 요청 사양은 도메인, 포트, 메서드, 프로토콜, 경로, 인자 및 기타 선택 항목을 포함해요). 두 샘플러의 PATH 필드에 같은 정보를 넣는 대신, 그 정보를 단일 구성 요소로 추출할 수 있어요. Interleave Controller가 "Search A"나 "Search B"의 요청을 "넘겨줄" 때 HTTP default request 구성 요소의 값으로 빈칸을 채워줘요. 그래서 그 요청들의 PATH 필드는 비워두고 정보는 구성 요소에 넣으면 돼요. 이 경우 이득이 크진 않지만 기능을 보여주는 예시예요. 트리의 다음 요소는 Thread Group 자체에 추가된 또 다른 HTTP default request예요. 스레드 그룹에는 내장 논리 컨트롤러가 있어서 설명한 대로 이 구성 요소를 사용해요. 통과하는 모든 Request의 빈칸을 채워줘요. 웹 테스트에서 모든 HTTP Sampler 요소의 DOMAIN 필드를 비워두고, 대신 그 정보를 Thread Group에 추가한 HTTP default request 요소에 넣는 건 매우 유용해요. 그러면 테스트 계획의 한 필드만 바꿔서 다른 서버에서 애플리케이션을 테스트할 수 있어요. 그렇지 않으면 모든 샘플러를 일일이 편집해야 해요. 마지막 요소는 HTTP Cookie Manager예요. Cookie Manager는 모든 웹 테스트에 추가해야 해요. 추가하지 않으면 JMeter가 쿠키를 무시하거든요. Thread Group 수준에 추가하면 모든 HTTP 요청이 같은 쿠키를 공유해요. 논리 컨트롤러는 조합해서 다양한 결과를 만들어낼 수 있어요.

3.2.3 테스트 프래그먼트 (Test Fragments) — Test Fragment 요소는 Thread Group 요소와 같은 수준으로 Test Plan 트리에 존재하는 특별한 종류의 컨트롤러예요. Module Controller나 Include Controller가 참조하지 않는 한 실행되지 않는다는 점에서 스레드 그룹과 구별돼요. 이 요소는 오로지 Test Plan 내 코드 재사용을 위한 거예요.

3.3 리스너 (Listeners)

리스너는 JMeter가 실행하는 동안 테스트 케이스에 대해 모은 정보에 접근하게 해줘요. Graph Results 리스너는 응답 시간을 그래프로 그려주고, "View Results Tree" 리스너는 샘플러 요청·응답의 세부 정보를 보여주며 응답의 기본 HTML·XML 표현을 표시할 수 있어요. 다른 리스너는 요약이나 집계 정보를 제공해요. 또한 리스너는 데이터를 나중에 쓰도록 파일로 보낼 수 있어요. JMeter의 모든 리스너는 데이터를 저장할 파일을 지정하는 필드를 제공하고, CSV 또는 XML 형식 중 무엇을 쓸지·어떤 필드를 저장할지 고르는 Configuration 버튼도 있어요. 모든 리스너는 같은 데이터를 저장하며, 차이는 화면에 표시하는 방식뿐이에요. 리스너는 테스트 어디에든 추가할 수 있고, 자기 수준 이하의 요소에서만 데이터를 수집해요. JMeter에는 여러 리스너가 들어 있어요.

3.4 타이머 (Timers)

기본적으로 JMeter 스레드는 샘플러를 쉬지 않고 순서대로 실행해요. Thread Group에 사용 가능한 타이머를 하나 추가해 지연을 지정하는 걸 권장해요. 지연을 추가하지 않으면 아주 짧은 시간에 요청을 너무 많이 만들어 JMeter가 서버를 압도할 수 있어요. 타이머는 자기 스코프 안의 각 샘플러 앞에서 일정 시간 지연시켜요. 스레드 그룹에 타이머를 하나 이상 추가하면 JMeter는 타이머 시간을 합산해 그만큼 멈췄다가 적용되는 샘플러를 실행해요. 타이머는 샘플러나 컨트롤러의 자식으로 추가해 적용되는 샘플러를 제한할 수도 있어요. 테스트 계획의 한 지점에서만 멈추게 하려면 Flow Control Action Sampler를 사용할 수 있어요.

3.5 어서션 (Assertions)

어서션은 테스트 대상 서버에서 받은 응답에 대해 사실을 주장하게 해줘요. 어서션을 쓰면 애플리케이션이 기대한 결과를 돌려주는지 본질적으로 "테스트"할 수 있어요. 예를 들어 응답에 특정 텍스트가 포함되어야 한다고 어서션할 수 있어요. 지정하는 텍스트는 Perl 스타일 정규표현식일 수 있고, 응답에 텍스트가 포함되어야 한다거나 응답 전체가 매치해야 한다고 지정할 수도 있어요. 어서션은 어떤 샘플러에도 추가할 수 있어요. 예를 들어 HTTP Request에 "" 텍스트가 있는지 확인하는 어서션을 추가할 수 있어요. 그러면 JMeter는 그 텍스트가 HTTP 응답에 있는지 확인하고, 찾지 못하면 실패한 요청으로 표시해요. 어서션은 자기 스코프 안의 모든 샘플러에 적용된다는 점에 유의하세요. 특정 샘플러로 한정하려면 그 샘플러의 자식으로 어서션을 추가하면 돼요. 어서션 결과를 보려면 Thread Group에 Assertion Listener를 추가해요. 실패한 어서션은 Tree View와 Table 리스너에도 표시되고, 예를 들어 Aggregate와 Summary 리포트에서 오류 %에 포함돼요.

3.6 구성 요소 (Configuration Elements)

구성 요소는 샘플러와 밀접하게 작동해요. 요청을 보내지는 않지만(HTTP(S) Test Script Recorder 제외) 요청에 추가하거나 수정할 수 있어요. 구성 요소는 요소를 놓은 트리 가지 안에서만 접근할 수 있어요. 예를 들어 Simple Logic Controller 안에 HTTP Cookie Manager를 놓으면, 그 Cookie Manager는 Simple Logic Controller 안에 놓은 HTTP Request Controller만 접근할 수 있어요. 또한 트리 가지 안의 구성 요소는 "부모" 가지의 같은 요소보다 우선순위가 높아요. 두 HTTP Request Defaults 요소 "Web Defaults 1"과 "Web Defaults 2"를 정의했다고 해볼게요. "Web Defaults 1"을 Loop Controller 안에 놓았으니 "Web Page 2"만 접근할 수 있고, 다른 HTTP 요청은 "Web Defaults 2"를 사용해요(이건 다른 모든 가지의 "부모"인 Thread Group에 놓았으므로). User Defined Variables 구성 요소는 달라요. 어디에 놓든 테스트 시작 시 처리돼요. 간단히 하려면 Thread Group 시작 부분에만 두는 걸 권장해요.

3.7 전처리기 요소 (Pre-Processor Elements)

Pre-Processor는 샘플러 요청이 만들어지기 전에 어떤 동작을 실행해요. 샘플러 요소에 붙으면 그 샘플러 요소가 실행되기 직전에 실행돼요. Pre-Processor는 주로 샘플 요청이 실행되기 직전에 설정을 수정하거나, 응답 텍스트에서 추출하지 않는 변수를 갱신할 때 사용해요.

3.8 후처리기 요소 (Post-Processor Elements)

Post-Processor는 샘플러 요청이 만들어진 후에 어떤 동작을 실행해요. 샘플러 요소에 붙으면 그 샘플러 요소가 실행된 직후에 실행돼요. Post-Processor는 주로 응답 데이터를 처리(흔히 값을 추출)할 때 사용해요.

3.9 실행 순서 (Execution order)

  1. 구성 요소 (Configuration elements)
  2. 전처리기 (Pre-Processors)
  3. 타이머 (Timers)
  4. 샘플러 (Sampler)
  5. 후처리기 (Post-Processors) — SampleResult가 null이 아닐 때
  6. 어서션 (Assertions) — SampleResult가 null이 아닐 때
  7. 리스너 (Listeners) — SampleResult가 null이 아닐 때

타이머, 어서션, 전·후처리기는 적용되는 샘플러가 있을 때만 처리된다는 점에 유의하세요. 논리 컨트롤러와 샘플러는 트리에 나타난 순서대로 처리돼요. 다른 테스트 요소는 발견된 스코프와 요소 유형에 따라 처리돼요. [같은 유형 안에서 요소는 트리에 나타난 순서대로 처리돼요.] 예를 들어 다음 테스트 계획에서

Controller
  Post-Processor 1
  Sampler 1
  Sampler 2
  Timer 1
  Assertion 1
  Pre-Processor 1
  Timer 2
  Post-Processor 2

실행 순서는 다음과 같아요.

Pre-Processor 1
Timer 1
Timer 2
Sampler 1
Post-Processor 1
Post-Processor 2
Assertion 1

Pre-Processor 1
Timer 1
Timer 2
Sampler 2
Post-Processor 1
Post-Processor 2
Assertion 1

3.10 스코핑 규칙 (Scoping Rules)

JMeter 테스트 트리에는 계층적이면서도 순서 있는 요소가 들어 있어요. 트리의 일부 요소는 엄격히 계층적이고(리스너, 구성 요소, Post-Processor, Pre-Processor, 어서션, 타이머), 일부는 주로 순서가 중요해요(컨트롤러, 샘플러). 테스트 계획을 만들 때 샘플러를 통해 실행할 일련의 단계를 나타내는 순서 있는 샘플 요청 목록을 만들게 돼요. 이런 요청은 흔히 순서가 있는 컨트롤러 안에 정리돼요. 요청 순서는 One, Two, Three, Four가 돼요. 어떤 컨트롤러는 하위 요소의 순서에 영향을 주는데, 이런 컨트롤러는 컴포넌트 레퍼런스에서 자세히 볼 수 있어요. 다른 요소는 계층적이에요. 예를 들어 어서션은 테스트 트리에서 계층적이에요. 부모가 요청이면 그 요청에 적용되고, 부모가 컨트롤러면 그 컨트롤러의 모든 하위 요청에 영향을 줘요. Assertion #1은 Request One에만 적용되고, Assertion #2는 Request Two와 Three에 적용돼요. 타이머를 쓴 또 다른 예시를 보면, 요청은 실행될 순서를 반영해 이름이 붙어 있어요. Timer #1은 Request Two, Three, Four에 적용되고(계층적 요소에는 순서가 무관하다는 점에 주목), Assertion #1은 Request Three에만, Timer #2는 모든 요청에 적용돼요. 구성(계층적) 요소가 어떻게 적용되는지는 이 예시들이 명확히 보여줘요. 각 Request가 트리 가지를 타고 부모로, 또 그 부모로 올라가면서 매번 그 부모의 구성 요소를 전부 수집한다고 상상해보면 동작 방식을 알 수 있어요. Header Manager, Cookie Manager, Authorization manager 구성 요소는 Configuration Default 요소와 다르게 취급돼요. Configuration Default 요소의 설정은 샘플러가 접근할 수 있는 값 집합으로 병합되지만, Manager의 설정은 병합되지 않아요. 샘플러의 스코프에 Manager가 여러 개 있어도 하나만 사용되고, 현재 어떤 것이 사용될지 지정할 방법은 없어요.

3.11 프로퍼티와 변수 (Properties and Variables)

JMeter 프로퍼티는 jmeter.properties에서 정의돼요(Getting Started - Configuring JMeter 참조). 프로퍼티는 jmeter 전체에 전역(global)이며, 주로 JMeter가 사용하는 일부 기본값을 정의하는 데 쓰여요. 예를 들어 remote_hosts 프로퍼티는 JMeter가 원격으로 실행하려는 서버를 정의해요. 프로퍼티는 테스트 계획에서 참조할 수 있지만(Properties ▶ read a property 함수 참조) 스레드별 값에는 사용할 수 없어요. JMeter 변수는 각 스레드에 로컬이에요. 값은 스레드마다 같을 수도, 다를 수도 있어요. 변수가 스레드에 의해 갱신되면 변수의 스레드 사본만 바뀌어요. 예를 들어 Regular Expression Extractor Post-Processor는 자기 스레드가 읽은 샘플에 따라 변수를 설정하고, 같은 스레드가 나중에 이 변수를 사용할 수 있어요. Test Plan과 User Defined Variables 구성 요소에서 정의한 값은 시작 시 전체 테스트 계획에 제공된다는 점에 유의하세요. 같은 변수를 여러 UDV 요소가 정의하면 마지막 것이 적용돼요. 스레드가 시작되면 초기 변수 집합이 각 스레드에 복사돼요. User Parameters Pre-Processor나 Regular Expression Extractor Post-Processor 같은 요소로 같은 변수를 재정의(또는 새로 만들기)할 수 있고, 재정의는 현재 스레드에만 적용돼요. setProperty 함수로 JMeter 프로퍼티를 정의할 수 있어요. 이 프로퍼티는 테스트 계획에 전역이라 필요하다면 스레드 간 정보를 전달하는 데 사용할 수 있어요. 변수와 프로퍼티 모두 대소문자를 구분해요.

3.12 변수로 테스트 파라미터화하기 (Using Variables to parameterise tests)

변수가 꼭 바뀔 필요는 없어요. 한 번 정의하고 건드리지 않으면 값을 바꾸지 않으니까요. 그래서 테스트 계획에 자주 나오는 표현의 축약형으로 쓸 수 있어요. 또는 실행 중에는 상수지만 실행 간에 달라질 수 있는 항목에 쓸 수 있어요. 예를 들면 호스트 이름이나 스레드 그룹의 스레드 수요. 테스트 계획을 구성할 때 실행 중 상수이지만 실행 간에 바뀔 수 있는 항목을 기록해두세요. 여기에 변수 이름을 정하는데, C_ 나 K_ 같은 접두사를 붙이거나 대문자만 쓰는 명명 규칙을 써서 테스트 중 바뀌는 변수와 구분할 수 있어요. 스레드에 로컬이어야 하는 항목(예: 카운터나 Regular Expression Post-Processor로 추출한 값)도 고려하세요. 여기에는 다른 명명 규칙을 쓰고 싶을 거예요. 예를 들어 Test Plan에 다음과 같이 정의할 수 있어요.

HOST www.example.com
THREADS 10
LOOPS 20

테스트 계획에서 ${HOST}, ${THREADS} 등으로 참조할 수 있어요. 나중에 호스트를 바꾸려면 HOST 변수의 값만 바꾸면 돼요. 테스트 수가 적을 때는 잘 동작하지만, 여러 조합을 테스트할 때는 지루해져요. 한 가지 해결책은 프로퍼티로 변수 값을 정의하는 거예요.

HOST ${__P(host,www.example.com)}
THREADS ${__P(threads,10)}
LOOPS ${__P(loops,20)}

그러면 명령줄에서 값의 일부 또는 전체를 다음과 같이 바꿀 수 있어요.

jmeter … -Jhost=www3.example.org -Jloops=13

더 알아보기