본문 바로가기
카테고리 없음

테스트 쉽게 하기1

by 경자꿈사 2024. 12. 31.

프로그램을 개발할 때 내가 작성한 프로그램이 올바르게 잘 동작하는지 테스트하는 것은 매우 중요한 일이다.
테스트와 관련해서 다뤄야 할 여러 가지 주제들이 있을 수 있지만, 이번 글에서는 단위 테스트에 대해 살펴보려고 한다.
단위 테스트는 메서드(또는 함수) 등 가장 작은 단위의 기능에 대해 수행하는 테스트로서, 가장 기본적인 테스트이면서 다른 모든 테스트의 기반이 되는 중요한 테스트이기도 하다.
메서드 자체에 대한 테스트 없이 로그인이나 상품 등록 등과 같이 여러 메서드 간의 호출 흐름으로 이루어지는 기능들을 테스트하는 건 바람직하지 않다.

프로그래밍 언어마다 테스트 작성 및 실행을 지원하는 라이브러리나 툴이 존재하는데, 루비에서도 표준 라이브러리에 포함된 Minitest를 사용하면 별도의 도구 없이 테스트를 쉽게 작성하고 실행할 수 있다.
아래 그림처럼 Minitest를 사용해서 간단한 테스트를 작성해 보자.
Minitest를 사용하여 테스트 코드를 작성하기 위해서는 먼저 Minitest 모듈에 정의된 기본 테스트 클래스인 Minitest::Test를 상속받는 클래스를 만들고 그 안에 'test_'로 시작하는 이름의 메서드를 작성하면 된다.
그리고 Minitest 모듈의 모듈 메서드인 run을 호출해 주면 작성한 클래스 안의 테스트 메서드들을 자동으로 실행시킨 후 전체 테스트 결과를 화면에 보여준다.
이 모든 것을 위해 제일 먼저 'minitest/autorun' 파일을 로드해야 한다.

TestCalc 클래스 안에 작성한 테스트 메서드를 살펴보면, assert_equal이라는 메서드를 사용하고 있는데 이것은 Minitest에서 제공하는 여러 어설션(Assertion) 메서드들 중 하나다.
아래 그림을 보면 Minitest::Test 클래스는 Minitest::Assertions 모듈을 인클루드하고 있고, Minitest::Assertions 모듈에는 어설션 메서드들이 정의되어 있는 게 보인다.

어설션 메서드들을 사용해서 실제 테스트 코드를 작성하는데, 앞의 예제에서 사용한 assert_equal 메서드는 기댓값(첫 번째 인수)과 실제 값(두 번째 인수)이 같은지 검증하는 메서드이다.
테스트 결과에서 보이는 '3 runs'는 실행한 테스트 메서드의 개수를 의미하고 '4 assertions'는 어설션 메서드를 사용해 검증을 수행한 횟수를 의미한다.
어설션 메서드를 호출하는 코드가 총 5개지만 test_sub 메서드 안의 두 번째 검증이 실패(기댓값과 실제 값이 다름)하면서 같은 메서드 안의 나머지 검증이 실행되지 않았기 때문에 실제 검증을 수행한 횟수는 4가 맞다.
그리고 '1 failures'은 검증이 실패한 테스트 메서드의 개수를 의미한다.
즉, 앞의 테스트 결과는 3개의 테스트 메서드를 실행하면서 4개의 검증을 수행했고 1개의 테스트 메서드가 실패했음을 의미한다.

이번에는 아래 그림처럼 예외가 발생하도록 검증 코드를 작성하고 테스트를 실행해 보자.
Minitest의 테스트 실행 결과를 보면 예외가 발생한 테스트 메서드의 이름과 어떤 예외가 발생했는지를 잘 표시해 준다. 그리고 마지막 라인에 '1 errors'도 볼 수 있다.

어떤 이유로 일부 테스트는 실행을 하지 않거나 또는 특정 조건일 경우에만 테스트를 실행하고 싶을 수도 있는데 이때 skip 메서드를 사용하면 된다.
물론 테스트 메서드의 이름을 skip_test_add처럼 변경하거나 주석 처리를 하면 실행되지 않겠지만, skip 메서드를 사용하면 테스트 실행 결과에 건너띈 테스트와 관련된 내용이 표시되기 때문에 유용하다.
이제 skip 메서드를 테스트해 보자. 

테스트 메서드 안에서 skip 메서드 호출 다음에 오는 모든 검증을 실행하지 않고 건너뛰는데, 메서드의 시작 부분에서 바로 skip 메서드를 호출했다고 해도 테스트 메서드 자체는 이미 실행이 되었기 때문에 실행한 테스트로 카운트된다.
그래서 앞의 테스트 결과를 보면 총 3개의 테스트 메서드가 실행되었다고 나오고 그중 2개의 테스트 메서드에서 검증을 건너띄었다고 표시해 준다.
앞의 테스트 결과들을 다시 보면 # Running: 아래 '.', 'F', 'E', 'S' 등의 문자가 보이는데 '.'은 테스트가 성공했음을 나타내고, 'F'는 실패를, 'E'는 테스트 실행 중 에러가 발생했고, 'S'는 테스트가 완료되지 않은 채 스킵되었음을 나타낸다.

그리고 Minitest는 테스트 메서드를 실행할 때 랜덤한 순서로 실행을 하는데, 이것은 테스트가 실행 순서에 영향을 받지 않고 독립적으로 잘 수행되는지 확인할 수 있게 해준다.
아래 그림을 보면 두 번의 테스트 실행에서 각각의 테스트 메서드의 실행 순서가 서로 다른 것을 볼 수 있다.
첫 번째 테스트의 실행 결과를 보면 'F.'으로 나오는데 이것은 test_sub가 먼저 실행된 후에 test_add가 실행되었다는 것을 의미한다. 반면에 두 번째 테스트에서는 '.F' 로 나오고 이것을 통해 test_add, test_sub 순으로 테스트 메서드가 실행되었음을 알 수 있다.

혹시나 실행되는 순서에 따라 테스트가 성공하거나 실패하는 경우가 발생할 경우, 해당 원인을 찾기 위해 동일한 순서로 테스트를 다시 실행해 볼 수 있어야 하는데 seed 옵션의 값을 동일하게 주고 테스트를 실행하면 항상 동일한 순서가 보장된다.
아래 코드를 D:/blog/ruby/test 폴더 아래 test_calc.rb 파일에 저장한 후 cmd 창을 열어 test_calc.rb를 실행해 보자.

require 'minitest/autorun'

class TestCalc < Minitest::Test
  def test_add
    assert_equal 5, 2 + 3
  end
  
  def test_sub
    assert_equal 1, 2 - 3
  end  

  def test_mul
    assert_equal 10, 2 * 5
  end
  
  def test_div
    assert_equal 1, 3 / 0
  end
end


출력된 내용의 첫 라인에 보이는 seed 값을 옵션으로 주고 test_calc.rb를 여러 차례 반복해서 실행해 보자. 계속해서 동일한 순서로 테스트 메서드가 실행되는 것을 볼 수 있을 것이다.

다음 글에서는 Minitest에서 제공하는 여러 가지 어설션 메서드들을 살펴보고, 테스트 메서드를 실행하기 전과 후에 필요한 작업들을 쉽게 처리할 수 있게 지원해 주는 기능도 함께 살펴보자.
See you again~~