✅ (선택) 루비(Ruby) 코드 품질 UP! 테스트 기초 배우기 (Part 11)

루비의 강력한 생태계, Gem과 Bundler의 세계는 어떠셨나요? 🌐 지난 Part 10: 루비 생태계 활용하기에서는 Gem을 통해 개발 생산성을 높이고 Bundler로 프로젝트를 안정적으로 관리하는 법을 배웠습니다. 이제 여러분의 코드를 더욱 신뢰할 수 있고, 자신감 있게 개선해 나갈 수 있도록 도와주는 중요한 기술, 바로 테스트(Testing)의 기초를 알아볼 시간입니다!

이번 파트는 커리큘럼 상 선택 사항이지만, 소프트웨어 개발에서 테스트는 매우 중요한 부분을 차지합니다. 테스트 코드를 작성하면:

  • ✅ 코드가 **의도한 대로 정확히 동작하는지** 확인할 수 있습니다.
  • ✅ 코드를 수정한 후에도 기존 기능이 **망가지지 않았는지(회귀, Regression)** 쉽게 검증할 수 있습니다.
  • ✅ 테스트 코드 자체가 **코드의 사용 예시이자 문서 역할**을 하기도 합니다.
  • ✅ 결과적으로 **코드 품질 향상**과 **개발 자신감 상승**으로 이어집니다! 💪

이번 시간에는 루비의 기본 테스트 프레임워크인 Minitest를 중심으로 테스트의 기본 개념과 간단한 테스트 코드를 작성하는 방법을 배워보겠습니다. 테스트의 세계로 함께 떠나볼까요?


🤔 테스트는 왜 중요할까요?

코드를 작성하다 보면 '이게 정말 제대로 동작할까?' 하는 불안감이 들 때가 있습니다. 특히 코드가 복잡해지거나 다른 부분을 수정했을 때, 예상치 못한 곳에서 문제가 발생하는 경우가 많죠.

테스트 코드는 이런 불안감을 해소하고 코드의 안정성을 확보하는 안전망 역할을 합니다.

  • 버그 조기 발견: 개발 과정에서 문제를 미리 찾아 수정 비용을 줄입니다.
  • 리팩토링 자신감: 코드를 개선하거나 구조를 변경할 때, 테스트를 통과하면 기존 기능이 유지된다는 확신을 가질 수 있습니다.
  • 협업 효율 증대: 다른 개발자가 작성한 코드를 이해하고 수정할 때 테스트 코드가 큰 도움이 됩니다.
  • 더 나은 설계 유도: 테스트하기 쉬운 코드를 작성하려고 노력하다 보면 자연스럽게 더 모듈화되고 깔끔한 설계를 하게 됩니다.

처음에는 테스트 코드를 작성하는 것이 추가적인 작업처럼 느껴질 수 있지만, 장기적으로는 훨씬 더 많은 시간과 노력을 절약해주고 코드 품질을 높여주는 중요한 투자입니다! 💰


🧪 루비 기본 테스트 도구: Minitest 시작하기

루비에는 여러 테스트 프레임워크가 있지만, 가장 기본적으로 내장되어 있고 가벼운 것이 바로 Minitest입니다. Minitest를 사용하여 간단한 단위 테스트(Unit Test)를 작성하는 방법을 알아봅시다. 단위 테스트는 코드의 가장 작은 단위(주로 메소드나 클래스)가 독립적으로 올바르게 작동하는지 검증하는 테스트입니다.

테스트 파일 구조

Minitest 테스트 코드는 보통 다음과 같은 구조를 가집니다.

  1. 테스트할 대상 코드를 require_relative (같은 디렉토리 내 파일 로드) 등으로 불러옵니다.
  2. minitest/autorunrequire 하여 Minitest 실행 환경을 설정합니다.
  3. 테스트하려는 클래스나 기능별로 테스트 클래스를 만듭니다. 이 클래스는 Minitest::Test 를 상속받아야 합니다.
  4. 테스트 클래스 안에 실제 테스트 내용을 담은 메소드들을 정의합니다. 메소드 이름은 반드시 test_ 로 시작해야 합니다!
  5. 각 테스트 메소드 안에서는 Minitest가 제공하는 다양한 단언(Assertion) 메소드를 사용하여 예상 결과와 실제 결과가 같은지 비교합니다.

간단한 예제: 계산기 클래스 테스트하기

간단한 덧셈 기능만 가진 Calculator 클래스를 만들고, 이를 테스트하는 코드를 작성해 봅시다.

1. 테스트 대상 코드 (`calculator.rb`):


# calculator.rb
class Calculator
  def add(a, b)
    a + b
  end
end
  

2. 테스트 코드 (`test_calculator.rb`): (같은 폴더에 저장)


# test_calculator.rb
require_relative 'calculator' # 1. 테스트 대상 파일 로드
require 'minitest/autorun'     # 2. Minitest 실행 환경 설정

# 3. 테스트 클래스 정의 (Minitest::Test 상속)
class CalculatorTest < Minitest::Test
  # 4. 테스트 메소드 정의 (test_ 로 시작)
  def test_add_two_positive_numbers
    # 테스트 준비 (Arrange)
    calculator = Calculator.new
    num1 = 3
    num2 = 5
    expected_result = 8

    # 테스트 실행 (Act)
    actual_result = calculator.add(num1, num2)

    # 결과 검증 (Assert)
    # 5. 단언 메소드 사용: 실제 결과(actual_result)가 예상 결과(expected_result)와 같은지 확인
    assert_equal(expected_result, actual_result, "3 + 5는 8이어야 합니다.")
  end

  def test_add_positive_and_negative_number
    calculator = Calculator.new
    assert_equal(-2, calculator.add(3, -5), "3 + (-5)는 -2여야 합니다.") # 준비, 실행, 검증을 한 줄에!
  end

  # 다른 테스트 케이스들을 추가할 수 있습니다...
  # def test_add_two_zeros
  #   ...
  # end
end
  

3. 테스트 실행하기:

터미널에서 테스트 파일이 있는 폴더로 이동한 후, 해당 루비 파일을 실행합니다.


ruby test_calculator.rb
  

테스트가 모두 통과하면 점(.)들이 찍히면서 성공 메시지가 나타납니다. 만약 실패(Assertion 실패)하면 `F`가, 오류가 발생하면 `E`가 표시되고 자세한 내용이 출력됩니다.

주요 단언(Assertion) 메소드

Minitest는 다양한 단언 메소드를 제공하여 예상 결과를 검증할 수 있도록 돕습니다.

  • assert_equal(expected, actual, [message]): 두 값이 같은지 확인 (가장 많이 사용!)
  • assert_nil(object, [message]): 객체가 nil인지 확인
  • assert_not_nil(object, [message]): 객체가 nil이 아닌지 확인
  • assert(test, [message]): 주어진 테스트 결과가 참(true)인지 확인
  • assert_raises(Exception) { ... }: 블록 안의 코드가 특정 예외를 발생시키는지 확인
  • assert_includes(collection, object, [message]): 컬렉션(배열 등)에 특정 객체가 포함되어 있는지 확인

테스트 상황에 맞는 적절한 단언 메소드를 사용하는 것이 중요합니다.


✨ 인기 있는 대안: RSpec 맛보기

Minitest 외에도 루비 커뮤니티에서 매우 인기 있는 테스트 프레임워크로 RSpec이 있습니다. RSpec은 BDD(Behavior-Driven Development, 행위 주도 개발) 스타일을 따르며, 좀 더 사람의 언어에 가까운 방식으로 테스트 코드를 작성할 수 있게 해줍니다.

RSpec 테스트 코드는 보통 describe, context, it, expect 와 같은 키워드를 사용하여 구성됩니다.

RSpec 예시 (`calculator_spec.rb`): (RSpec 젬 설치 필요: `gem install rspec`)


# calculator_spec.rb
require_relative 'calculator' # 테스트 대상 로드

RSpec.describe Calculator do
  describe "#add" do # 테스트 대상 메소드 명시
    it "두 양수를 올바르게 더한다" do # 테스트 시나리오 설명
      calculator = Calculator.new
      expect(calculator.add(3, 5)).to eq(8) # expect(실제값).to eq(기대값) 형태
    end

    it "양수와 음수를 올바르게 더한다" do
      calculator = Calculator.new
      expect(calculator.add(3, -5)).to eq(-2)
    end
  end
end
  

테스트 실행은 터미널에서 `rspec calculator_spec.rb` 명령어를 사용합니다.

RSpec은 Minitest보다 좀 더 풍부한 기능과 표현력을 제공하지만, 처음에는 약간 더 학습 곡선이 있을 수 있습니다. 어떤 프레임워크를 선택할지는 개인의 선호나 팀의 컨벤션에 따라 달라집니다. Minitest로 시작해서 테스트에 익숙해진 후 RSpec을 배워보는 것도 좋은 방법입니다.


🎮 오늘의 실습: 이전 코드 테스트 작성하기!

이전 파트들에서 만들었던 메소드 중 하나를 골라 Minitest로 테스트 코드를 작성해 봅시다! 예를 들어, Part 5에서 만든 add 또는 subtract 메소드, 또는 Part 8에서 만든 repeat 메소드 등이 좋습니다.

도전 과제:

  1. 테스트하고 싶은 코드가 담긴 루비 파일(예: `my_methods.rb`)을 준비합니다.
  2. 같은 폴더에 테스트 파일(예: `test_my_methods.rb`)을 만듭니다.
  3. 테스트 파일에 필요한 `require` 구문과 테스트 클래스를 작성합니다.
  4. 선택한 메소드의 동작을 검증하는 `test_` 로 시작하는 테스트 메소드를 하나 이상 작성합니다.
  5. 테스트 메소드 안에서 assert_equal 등 적절한 단언 메소드를 사용하여 결과를 검증합니다.
  6. 터미널에서 테스트 파일을 실행하여 통과하는지 확인합니다!

💡 Part 11 핵심 요약

  • 테스트의 중요성: 코드 정확성 검증, 회귀 방지, 리팩토링 자신감, 문서 역할, 품질 향상.
  • Minitest: 루비 기본 내장 테스트 프레임워크. 가볍고 간단함.
    • 테스트 파일 구조: `require_relative`, `require 'minitest/autorun'`, `Minitest::Test` 상속 클래스, `test_` 시작 메소드, 단언(Assertion).
    • 실행: `ruby 테스트파일.rb`
    • 주요 단언: assert_equal, assert_nil, assert, assert_raises 등.
  • RSpec: 인기 있는 BDD 스타일 테스트 프레임워크. 자연어 유사 문법 (describe, it, expect).

🚀 다음 단계로!

테스트의 기초를 배우신 것을 축하합니다! 🎉 비록 선택 파트였지만, 테스트는 더 나은 개발자로 성장하는 데 정말 중요한 습관입니다. 당장은 조금 번거롭게 느껴지더라도 간단한 테스트부터 꾸준히 작성하는 연습을 해보시길 바랍니다. 코드를 작성하는 것만큼이나 테스트하는 것의 중요성을 꼭 기억해주세요!

이제 루비 학습 로드맵의 마지막 정규 파트입니다! 다음 Part 12: 루비로 무엇을 할까? & 다음 여정 에서는 지금까지 배운 루비 지식을 바탕으로 어떤 멋진 일들을 할 수 있는지 알아보고, 앞으로의 학습 방향을 설정하는 시간을 가질 거예요. 루비 온 레일즈, 자동화 스크립트 등 흥미로운 세계가 기다리고 있습니다! 😉