테스트 프레임워크가 개발자의 테스트 작성 부담을 줄이지 못한다면 그 역할을 다하지 못하는 것이라는 저자의 관점에서, RSpec과 Minitest의 여정을 통해 ‘Intuitive Expectations’의 필요성을 설명합니다.
RSpec 비판
RSpec은 겉보기에는 멋진 Ruby DSL처럼 보이지만, DMMT 원칙을 심각하게 위반한다고 지적합니다. 다음은 주요 비판점입니다.
-
expect(actual).to be_within(delta).of(expected)와 같은 문법에서.to의 존재 이유와not_to를 함께 기억해야 하는 부담. -
be_*로 시작하는 매처들의 불일치성 및 비문법적인 표현 (satisfy매처 예시). -
특정 매처에서만 사용되는
.of와 같은 일회성 DSL 요소가 ‘최소 놀람의 원칙’을 위반.
Minitest Assertions의 장점과 한계
RSpec의 복잡성에 질린 저자는 Minitest Assertions의 간결함(assert something_is)에 매력을 느낍니다. 이는 DMMT 원칙의 훌륭한 예시로 평가됩니다. 그러나 다음과 같은 한계도 존재합니다.
-
assert_equal에서 예상 값(expected)이 실제 값(actual)보다 먼저 오는 ‘역방향’ 인자 순서 문제. -
수많은
assert_*또는refute_*문장으로 인해 테스트 코드가 장황해지는 문제. -
describe/it와 같은 스펙 스타일 테스트 작성을 선호하지만, Assertions만으로는 부족함을 느낌.
Minitest Expectations의 도입과 개선 필요성
Minitest 자체적으로 제공하는 Expectations(expect(first_name).must_equal "Jared")는 must_*와 wont_* 매처를 사용하여 스펙 스타일 테스트에 적합합니다. 예외 발생 여부 확인과 같은 블록 내 코드 테스트에도 유용합니다. 그러나 저자는 여전히 새로운 must_* 또는 wont_* 매처를 학습해야 하는 부담이 DMMT 원칙에 위배된다고 생각합니다. Ruby의 가장 표준적이고 명확한 언어 코드를 사용하여 테스트를 작성할 수 있어야 한다는 질문에서 ‘Intuitive Expectations’가 탄생합니다.
Intuitive Expectations 소개
Bridgetown Foundation gem의 새로운 기능인 ‘Intuitive Expectations’는 Minitest의 Expectations를 기반으로 더욱 단순하고 체인 가능한(chainable) 변형을 제공합니다. 이는 Ruby의 일반적인 연산자와 메서드를 활용하여 직관적인 테스트 구문을 구현합니다.
-
expect(some_int) != 123 -
expect(some_big_str) << "howdy"(또는include?) -
expect(some_bool).true? -
expect(2..4).within?(1..6) -
expect(food_tastes) =~ /g(r+)eat/체인 가능한 DSL을 통해 여러 기대를 연속적으로 표현할 수 있습니다. -
expect(big_string).include?("foo").include?("bar").exclude?("baz") -
expect(user).is?(:moderator?).isnt?(:admin?)대부분의 명명된 매처는match?와not_match?처럼 ‘not’ 쌍을 가지고 있어 쉽게 유추할 수 있습니다.
활용 예시 및 확장
Bridgetown Core 팀은 기존의 Jekyll 포크 기반 테스트 스타일에서 Minitest 스펙 스타일과 ‘Intuitive Expectations’로 전환하고 있습니다. 실제 테스트 코드 예시를 통해 그 간결함과 가독성을 보여줍니다. 저자는 이 문법이 너무나 익숙하고 편리하여, JavaScript 환경에서도 Node의 내장 Assertions와 테스트 기능을 기반으로 유사한 ‘Intuitive Expectations’ 버전을 구현했습니다.