Test Code

GreenBean·2022년 9월 27일
0
post-thumbnail
post-custom-banner

Test Code

Test Code 란?

  • 테스트 코드란 소프트웨어의 제품 또는 서비스의 품질을 확인하거나 소프트웨어의 버그를 찾을 때 작성하는 코드를 의미
    • 다시 말해 제품이 예상하는 대로 동작 하는지 확인하는 것
  • Test Code 작성의 장점
    • 개발 과정 중 예상치 못한 문제를 미리 발견할 수 있음
    • 작성한 코드가 의도한 대로 작동하는지 검증할 수 있음
    • 코드 변경에 대한 사이드 이펙트를 줄이는 예방책
    • 코드 수정이 필요한 상황에서 유연하고 안정적인 대응을 할 수 있도록 해줌
    • 코드의 모듈화를 한번 더 고민하게 해줌
    • 코드 변경 시 변경 부분으로 인한 영향도를 쉽게 파악할 수 있음
    • 코드 리팩토링 시 기능 구현이 동일하게 되었다는 판단을 내릴 수 있음
    • 테스트 코드를 통해 동작하는 방식 및 결과 확인 가능

Tip! Test 의 종류

  • 테스트 대상 범위나 성격에 따라 크게 세 가지로 구분
    • UI Test, Integration Test, Unit Test
  • Unit Test: 단위 테스트
    • 소형 테스트에 속하는 테스트
    • 클래스 범주 내에서 작은 단위 (함수) 의 기능에 대한 유효성을 검증하는 테스트
    • 단위 테스트는 매우 간단하고 명확하며 빠르게 실행된다는 특징 있음
    • 하나의 함수에 대한 하나 이상의 테스트가 존재할 수 있고 각각의 조건에 대한 유효성을 검증할 수 있음
    • 이렇게 작성된 테스트가 많을수록 해당 로직에 대한 신뢰도가 높아질 수 있음
    • 작게 쪼개진 단위 테스트는 해당 로직이 어떤 역할을 하는지 쉽게 파악할 수 있음
  • Integration Test: 통합 테스트
    • 중형 테스트에 속하는 테스트
    • 서로 다른 모듈 혹은 클래스 간 상호작용의 유효성을 검사하는 테스트
    • 각각 단위 테스트가 검증되었다 하더라도 모듈 간 인터페이스 및 데이터 흐름이 의도한 대로 작동하지 않는 경우도 있기 때문에 조금 더 넓은 범위에서 추가적인 테스트가 필요
    • 또한 각 모듈에 대한 설정 또는 테스트를 하기 위해 사전 조건이 필요한 경우도 있기 때문
    • 단위 테스트보다 테스트 코드를 작성하기가 복잡하지만 단위 테스트 보다 더 넓은 범위의 종속성까지 테스트함으로써 단위 테스트 보다 좀 더 유의미한 테스트가 되는 경우가 많음
  • UI Test
    • 대형 테스트로 분류되는 테스트
    • 실제 사용자들이 사용하는 화면에 대한 테스트를 하여 서비스의 기능이 정상적으로 작동하는지 검증하는 테스트
    • UI 테스트는 실제 앱을 사용하는 사용자의 흐름에 대해 테스트 함으로써 UI 변경 사항으로 발생할 수 있는 문제를 사전에 차단
    • 화면과 직접적으로 연관되어있는 테스트이기 때문에 실행 시간도 오래 걸리고 디자인이 변경될 때마다 테스트 코드의 수정이 필요하여 유지보수 비용이 큼

Test Coverage 란?

  • 테스트 커버리지란 시스템 또는 소프트웨어의 테스트를 논할 때 얼마나 테스트가 충분한가를 나타낸 것
    • 즉, 수행한 테스트테스트의 대상을 얼마나 커버했는지를 나타냄

코드 커버리지

  • 코드 커버리지는 소스 코드를 기반으로 수행하는 화이트 박스 테스트를 통해 측정

Tip!

  • 블랙 박스 테스트 (Black-box test)
    • 소프트웨어의 내부 구조나 작동 원리를 모르는 상태에서 동작을 검사하는 방식
    • 올바른 입력과 올바르지 않은 입력을 입력하여 올바른 출력이 나오는지 테스트하는 기법
    • 사용자 관점의 테스트 방법이라 볼 수 있음
  • 화이트 박스 테스트 (White-box test)
    • 응용 프로그램의 내부 구조와 동작을 검사하는 테스트 방식
    • 소프트웨어 내부 소스 코드를 테스트하는 기법
    • 개발자 관점의 단위 테스트 방법이라 볼 수 있음

커버리지를 측정하는 기준

  • 먼저 코드의 구조를 살펴보면 크게 구문 (Statement), 조건 (Condition), 결정 (Decision) 의 구조로 이루어져 있음
  • 코드 커버리지는 이러한 코드의 구조를 얼마나 커버했느냐에 따라 측정 기준이 나뉘게 됨

구문 커버리지(Statement Coverage)

  • 라인 (Line) 커버리지라고 부르기도 함
  • 코드 한 줄이 한 번 이상 실행된다면 충족
# 예시 코드
def test(x):
	print("start line") # 1번
    if x > 0: # 2번
    	print("middle line") # 3번
    print("last line") # 4번
  • 위의 코드를 테스트한다고 가정했을 때
    • x = -1 을 테스트 데이터로 사용할 경우
    • if 문의 조건을 통과하지 못하기 때문에 3번 라인의 코드는 실행되지 못함
    • 총 4개의 라인에서 1 → 2 → 4번의 라인만 실행되므로 구문 커버리지3 / 4 * 100 = 75(%) 가 됨

조건 커버리지(Condition Coverage)

  • 여기서의 조건은 모든 조건식
  • 모든 조건식의 내부 조건이 true 또는 false 의 경우를 충족하는 지 확인
# 예시 코드
def test(x, y):
	print("start line") # 1번
    if x > 0 and y < 0: # 2번
    	print("middle line") # 3번
    print("last line") # 4번
  • 여기서 내부 조건이란 조건 식 내부의 각각의 조건
    • 즉, x > 0y < 0 를 말하며 각각이 true 또는 false의 경우가 있으면 조건 커버리지를 만족
  • 위의 코드를 테스트한다고 가정했을 때
    • x = 1, y = 1x = -1, y = -1 을 테스트 데이터로 사용할 경우
    • 내부 조건식 x > 0y < 0 는 모두 양수일 때와 음수일 때 테스트 케이스가 존재하므로 조건 커버리지를 만족
    • 하지만 x > 0 and y < 0 는 두 경우 모두 false 이므로 2번 라인의 코드는 테스트가 안되는 결과가 발생
  • 이처럼 테스트를 작성했을 때 조건 커버리지를 만족하더라도 구문 커버리지와 이후에 나올 결정 커버리지를 만족하지 못하는 경우가 존재

결정 커버리지(Decision Coverage)

  • 브랜치 (Branch) 커버리지라고 부르기도 함
  • 모든 조건식이 true 또는 false를 가지게 되면 충족
# 예시 코드
def test(x, y):
	print("start line") # 1번
    if x > 0 and y < 0: # 2번
    	print("middle line") # 3번
    print("last line") # 4번
  • 위의 코드를 테스트한다고 가정했을 때
    • x = 1, y = 1x = -1, y = -1 을 테스트 데이터로 사용할 경우
    • 조건 커버리지가 만족되더라도 2번 라인의 코드가 실행되지 않았던 결과가 있었음
    • 결정 커버리지에서 결정은 내부 조건이 아닌 조건식을 말하는데 위의 코드에서는 x > 0 and y < 0 를 말함
  • 따라서 결정 커버리지를 만족하는 테스트를 만든다고 한다면 x = 1, y = 1x = -1, y = -1 와 같이 조건식이 false 만 나오는 식이 아닌 x = 1, y = -1 를 넣는 테스트를 만들어야함
    • 이 테스트 케이스를 넣게되면 조건 커버리지와 결정 커버리지, 구문 커버리지를 모두 만족할 수 있게 됨

가장 많이 사용되는 코드 커버리지

  • 위에 세 가지 코드 커버리지 중에서 구문 커버리지가 가장 대표적으로 많이 사용되고 있음
  • 그 이유는 조건 커버리지나 브랜치 커버리지의 경우 코드 실행에 대한 테스트보다는 로직의 시나리오에 대한 테스트에 더 가깝다고 볼 수 있기 때문
    • 위에 두 커버리지는 조건문이 존재하지 않는 코드의 경우 그 코드는 커버리지 대상에서 아예 제외를 함
    • 즉, 해당 코드들을 테스트를 하지 않음
  • 그러나 구문 커버리지를 만족한다면 모든 코드를 테스트 코드가 커버했다고는 말할 수 있는 있게 됨
    • 물론 위에 결정 커버리지의 코드 예시에서 조건식이 false 인 시나리오에 대해서 테스트 됐다고 보장할 수 없지만 그래도 조건문 내부의 코드가 실행되었을 때 문제가 없다는 것은 보장할 수 있음
  • 정리하면 구문 커버리지를 만족하면 모든 시나리오를 테스트한다는 보장은 할 수 없지만 어떤 코드가 실행되더라도 해당 코드는 문제가 없다는 보장은 할 수 있음
    • 이런 이유로 구문 커버리지를 더 많이 사용함

Test Code Tip

Given, When, Then

  • 테스트 코드 작성 시 많은 곳에서 추천하는 코딩 스타일
  • 어떤 값이 주어지고 (Given), 무엇을 했을 때 (When), 어떤 값을 원한다 (Then) 을 나누어 직관적으로 볼 수 있기 때문에 코드의 가독성이 향상됨
  • 테스트 코드의 가독성이 중요한 또 다른 이유는 테스트 코드가 문서로써의 역할을 하기도 하기 때문
    • 테스트 코드를 봄으로써 해당 메소드를 작성한 개발자가 어떤 의도로 만들었으며 어떻게 동작하길 원하는지를 알 수 있음

모든 응답에 대한 테스트를 진행

  • API가 조금이라도 수정될 경우, 테스트 코드가 실패하게 됨으로써 항상 올바른 테스트 코드를 유지할 수 있도록 도움
  • API가 변경되면 테스트 코드 역시 변경되어야 하는 것이 당연
  • 테스트 코드 커버리지는 높을수록 좋음
    • 테스트 코드는 정상적으로 작동하는 부분만을 테스트하면 안됨
    • 테스트 코드는 실수나 오류를 발견하고 이를 줄이고 수정하기 위해 작성하는 것

F.I.R.S.T

  • Fast
    • 단위 테스트는 가능한 빠르게 실행되어야 함
    • 실행함에 있어 너무 느려 테스트를 꺼리게 된다면 잘못된 단위 테스트
  • Independent
    • 단위 테스트는 객체의 상태, 메소드, 이전 테스트 상태, 다른 메소드의 결과 등에 의존해서는 안됨
    • 따라서 단위 테스트는 어떠한 순서로 실행되더라도 성공해야 함
  • Repeatable
    • 단위 테스트는 반복 가능해야 함
    • 데이터베이스에 의존하는 테스트는 테스트 수행 후 자동으로 롤백을 한다는 등의 별도 설정이 필요
  • Self-Validating
    • 단위 테스트는 자체 검증이 가능해야 함
    • 테스트를 개발자가 직접 수동으로 확일할 필요 없이 assert 문 등에 의해 성공 여부가 결과로 나타나야 함
  • Timely
    • 단위 테스트를 통과하는 제품 코드가 작성되기 바로 전에 단위 테스트를 작성해야 함
    • TDD를 하고 있다면 적용이 되지만 그렇지 않을 수도 있음

Tip! Python Coverage 패키지 관련 내용
python coverage with unittest 적용하기
Python test code coverage 방법
테스트 코드 커버리지 측정 - coverage

profile
🌱 Backend-Dev | hwaya2828@gmail.com
post-custom-banner

0개의 댓글