BDD로 — 「행위」에 주목한 테스트의 어휘 혁명
ThoughtWorks에서 TDD를 실천하고 코칭하던 댄 노스(Dan North)는, 초보자가 반복해서 겪는 혼란의 뿌리가 「테스트」라는 말 자체에 있음을 깨달았다. 2003년 JBehave에서 시작해 2006년 논문으로 BDD를 체계화하고, RSpec과 Cucumber/Gherkin을 통해 「실행 가능한 명세」를 쓰는 문화가 퍼져나갔다. Given-When-Then이라는 어휘가 업계를 가로질러 자리 잡기까지의 경위와 그 공과를 따라간다.
행위 주도 개발(Behaviour-Driven Development, BDD)은 테스트 주도 개발(TDD)의 실천에서 태어났지만, TDD와는 겉모습만 닮은 다른 방법론이다.
이를 만든 댄 노스(Dan North, 정식 이름 다니엘 테르호스트노스Daniel Terhorst-North)는 TDD를 가르치는 과정에서 같은 의문에 거듭 부딪혔다. 「어디까지 테스트해야 하는가」 「이것을 테스트라고 불러야 하는가」 「무엇을 가장 먼저 테스트해야 하는가」 — 이는 기술적인 문제가 아니라 「테스트(test)」라는 말이 일으키는 어휘상의 혼란임을 노스는 깨달았다. 이 깨달음에서 「테스트」가 아니라 「행위(behaviour)」를 어휘의 중심에 두는 새로운 실천이 태어났다. 그것이 BDD다. BDD는 이윽고 JBehave, RSpec, Cucumber라는 세 가지 중요한 도구를 통해 구체화되었고, Gherkin 표기법과 Given-When-Then이라는 구조화된 자연어로 「실행 가능한 명세(executable specification)」를 쓰는 문화를 낳았다.
TDD의 「test」라는 어휘가 낳은 오해
2000년대 전반, 노스는 ThoughtWorks에서 TDD의 실천과 코칭을 맡고 있었다. 그가 거듭 관찰한 것은 TDD 초보자가 품는 근원적인 망설임이었다.
- 테스트 메서드명을
testXxx로 써야 하는지, 무엇을 테스트한다고 생각해야 하는지 모른다 - 「단위 테스트」와 「인수 테스트」의 경계가 모호하다
- 「테스트 퍼스트」라는 말을 들어도, 아직 존재하지 않는 기능에 대해 무엇을 검증해야 할지 모른다
- 테스트가 늘어날수록 「테스트를 위한 테스트」처럼 보여, 비즈니스 가치와의 연결을 잃어버린다
노스는 이러한 문제의 뿌리가 기술이 아니라 말에 있다고 생각했다. 「테스트」라는 말은 「검증」을 연상시켜, 「앞으로 무엇을 만들려 하는가」라는 설계·명세의 측면을 가려버린다. 그래서 노스는 테스트 코드의 명명을 test~가 아니라 should~(~해야 한다)로 바꿀 것을 제안했다. 이로써 초점이 「검증」에서 「기대되는 행위의 기술」로 옮겨간다.
JBehave의 탄생(2003년)
2003년 말, 노스는 JUnit을 대신할, 테스트라는 어휘를 일절 배제한 프레임워크 JBehave를 쓰기 시작했다. JBehave는 자바 단위 테스트의 문법을 「행위 명세(behaviour specification)」로 다시 쓰는 것을 노린 것으로, 2004년에 걸쳐 발전했다. 이 초기 시도에는 훗날 BDD의 발전에 관여하는 리즈 케오(Liz Keogh) 와 크리스 매츠(Chris Matts) 도 이른 단계부터 참여했다.
2004년 말, 노스가 크리스 매츠에게 자신의 「행위 중심 어휘」를 설명하고 있을 때, 매츠가 「그거 그냥 분석(analysis) 아니야?」라고 지적한 일화는, BDD가 단위 테스트 기법에서 요구 분석·인수 기준 기법으로 확장되어 가는 전환점으로 알려져 있다. 매츠는 이 흐름 속에서 비즈니스 가치를 출발점으로 기능을 도출하는 피처 인젝션(Feature Injection) 이라는 기법을 고안했다.
“Introducing Behaviour-Driven Development”(2006년)
노스는 2006년 3월, 업계지 『Better Software』에 BDD를 체계적으로 소개하는 논문 “Introducing Behaviour-Driven Development” 를 발표했다. 이 논문에서 BDD는 다음과 같이 정리되었다.
| TDD의 용어 | BDD에서의 재구성 |
|---|---|
| 테스트를 쓴다(test) | 행위를 기술한다(specify behaviour) |
테스트 메서드명 testXxx |
should~ / it~ 형식의 문장 |
| 단위 테스트와 인수 테스트 구분이 모호함 | 「개발자용 행위」와 「비즈니스용 행위」를 명확히 분리 |
| 테스트가 문서가 된다(부산물) | 명세서로서 처음부터 쓴다(1차 산출물) |
이 논문에서 Given-When-Then이라는 인수 기준 기술 템플릿도 소개되었다. 「Given(전제 조건)」 「When(조작·이벤트)」 「Then(기대되는 결과)」라는 삼부 구성은, 도메인 주도 설계(DDD)에서 에릭 에반스(Eric Evans)가 제창한 유비쿼터스 언어(ubiquitous language) — 개발자와 도메인 전문가가 같은 어휘를 공유해야 한다는 사고방식 — 의 영향을 강하게 받았다.
RSpec의 탄생과 발전
JBehave가 자바 세계에서의 실험이었다면, BDD의 사상을 루비 세계에서 본격적으로 꽃피운 것이 RSpec이다.
- 2005년 — 스티븐 베이커(Steven Baker)가 실험 삼아 RSpec을 시작. 데이브 아스텔스(Dave Astels)와 아슬라크 헬레쇠이(Aslak Hellesøy)가 초기 기여자로 참여
- 2006년 여름 — 데이비드 첼림스키(David Chelimsky)가 프로젝트에 참여, 훗날 리더십을 이어받음
- 2007년 5월 — RSpec 1.0 릴리스.
describe블록으로 대상을,it블록으로 기대되는 행위를 기술하는 구문이 확립됨
describe Order do
it "is invalid when the total is negative" do
order = Order.new(total: -10)
expect(order).not_to be_valid
end
end
이 describe/it/expect라는 구문은 「테스트 케이스」가 아니라 「명세(spec)」를 쓴다는 경험을 제공해, BDD의 어휘를 코드 수준에서 구현했다. 2009년에는 첼림스키와 아스텔스를 중심으로 브라이언 헬름캠프(Bryan Helmkamp), 댄 노스, 잭 데니스(Zach Dennis), 아슬라크 헬레쇠이의 공저로 『The RSpec Book: Behaviour Driven Development with RSpec, Cucumber, and Friends』 가 출간되어 BDD의 실천 지식이 체계화되었다.
Cucumber의 탄생과 Gherkin 표기법
RSpec의 초기 기여자였던 아슬라크 헬레쇠이는 자연어에 가까운 형식으로 인수 기준을 쓰고, 이를 그대로 실행 가능한 테스트로 동작시키는 도구 Cucumber를 개발했다. Cucumber는 루비용으로 시작해, 훗날 여러 언어(Cucumber-JVM, .NET용 SpecFlow, Python용 behave 등)로 확산되었다.
Cucumber의 핵심이 Gherkin 표기법이다. Gherkin은 Given-When-Then의 구조를 비즈니스 담당자도 읽고 쓸 수 있는 자연어풍 텍스트로 형식화한 것이다.
Feature: Applying a discount coupon
Scenario: A valid coupon is discounted
Given the cart total is 3000 yen
And I have a valid 10% discount coupon
When I apply the coupon
Then the total becomes 2700 yen
이 시나리오는 그대로 「스텝 정의(step definition)」라 불리는 코드에 연결되어 자동 테스트로 실행된다. 이로써 비즈니스 측이 읽을 수 있는 명세서와 실제로 동작하는 테스트 코드가 (이론상으로는) 일치하는 상태를 유지할 수 있게 되었다.
BDD 프레임워크의 다언어 확산
Cucumber/Gherkin 모델은 루비 이외의 언어 생태계로도 급속히 퍼져나갔다.
| 언어·환경 | 도구 | 비고 |
|---|---|---|
| Ruby | Cucumber, RSpec | 원조. Gherkin과 RSpec풍 구문이 함께 공존 |
| Java / JVM | Cucumber-JVM, JBehave | JBehave는 노스 자신이 낳은 원류 |
| .NET | SpecFlow | Gherkin을 .NET에서 실행, 후계는 Reqnroll |
| Python | behave, pytest-bdd | Gherkin풍 .feature 파일을 pytest와 통합하는 것도 있음 |
| JavaScript | Cucumber.js, jest-cucumber | Jest 위에서 describe/it에 Gherkin 시나리오를 흘려넣는 형태도 보급 |
이들은 모두 「Given-When-Then」이라는 공통 어휘와 Gherkin풍 표기법을 토대로 하고 있으며, BDD가 특정 언어의 유행에 그치지 않고 테스트 표기의 공통 언어로 자리 잡았음을 보여준다.
Specification by Example과 Example Mapping
BDD의 실천이 확산되는 가운데, 「Given-When-Then 시나리오를 어떻게 협업으로 도출할 것인가」라는 발견(discovery) 과정 자체를 체계화하려는 움직임도 나타났다. 고이코 아지치(Gojko Adzic)는 2011년, 약 50개 프로젝트 사례를 바탕으로 한 저서 『Specification by Example: How Successful Teams Deliver the Right Software』 를 출간하여, 「구체적인 예시에 의한 명세화」라는 BDD에 가까운 실천 지식을 체계화했다(이 책은 2012년 Jolt Award를 수상했다).
또한 Cucumber사의 맷 윈(Matt Wynne)은 사용자 스토리를 Gherkin 시나리오로 옮기기 전 단계로서 Example Mapping이라는 경량 워크숍 기법을 고안했다. 포스트잇을 사용해 「스토리(노랑)」 「규칙(파랑)」 「구체 예시(초록)」 「의문점(빨강)」의 4색으로 짧은 시간(약 25분) 안에 정리하는 이 기법은, Three Amigos의 논의를 발산시키지 않고 구체적인 예시로 수렴시키는 실천으로 널리 채택되고 있다.
유비쿼터스 언어·인수 기준·Three Amigos
BDD가 단순한 테스트 기법을 넘어 개발 프로세스론으로 확산된 배경에는 다음과 같은 실천이 있다.
- 유비쿼터스 언어와의 연결 — DDD의 유비쿼터스 언어와 마찬가지로, Gherkin 시나리오는 비즈니스·개발·QA가 공유하는 어휘의 그릇이 된다
- Three Amigos(삼자 회의) — 요구 사항을 굳힐 때 비즈니스 분석가(BA)·개발자(Dev)·테스터(QA) 세 사람이 모여 Given-When-Then 형태로 시나리오에 합의하는 관습. BDD가 「테스트 자동화 기법」이 아니라 「요구 분석·협업 기법」임을 보여주는 전형적인 예다
- 인수 테스트 주도 개발(ATDD)과의 관계 — ATDD도 비즈니스 측과 합의한 인수 기준에서 테스트를 도출한다는 점에서 BDD와 겹치지만, BDD는 여기에 「행위」라는 통일 어휘와 Gherkin이라는 표기법을 가져온 점이 독자성이다
BDD의 공과
BDD의 보급은 많은 현장에 「명세와 일치하는 테스트」라는 이상을 가져왔지만, 실천상의 폐해도 널리 지적되기에 이르렀다.
- 과도한 Gherkin화 — 모든 테스트를 Gherkin으로 쓰려 한 결과, 단위 테스트 수준의 사소한 검증까지 자연어의 오버헤드를 짊어져 가독성이 오히려 낮아지는 사례가 빈발했다
- 이중 유지보수 비용 —
.feature파일(시나리오)과 스텝 정의 코드 양쪽을 모두 유지보수해야 하며, 리팩터링 시 동기화가 무너지기 쉽다 - 「자연어풍」의 취약함 — 미묘한 표현 차이로 스텝 정의가 매칭되지 않거나, 문구의 통일이 특정 개인에게 의존하게 되는 문제
- 비즈니스 측이 실제로는 읽지 않음 — 이상적으로는 비개발자가
.feature파일을 읽고 쓰는 것이지만, 실제로는 개발자·QA만이 유지보수하는 「테스트를 위한 독자적 DSL」이 되어 있는 현장도 많다 - 댄 노스 자신의 재고 — 노스는 훗날 BDD의 본질은 「Given-When-Then이라는 표기법」이 아니라 「행위에 초점을 맞추는 사고방식」이라고 거듭 강조하며, Gherkin의 기계적인 적용에 경종을 울리고 있다
현재의 위치
오늘날 BDD 프레임워크(Cucumber, SpecFlow, behave, Jest-Cucumber 등)는 E2E·인수 테스트 계층에서 사용되는 경우가 많고, 단위 테스트 수준에서는 「BDD 스타일 어서션」(describe/it 구문은 쓰지만 자연어 DSL은 쓰지 않는, Jest·Mocha·RSpec 등)으로서 어휘만이 느슨하게 자리 잡고 있다. BDD는 「모든 것을 Gherkin으로 쓴다」는 운동으로서는 후퇴하고 있지만, 「테스트는 명세이며 행위를 기술하는 것이다」라는 사상 그 자체는 xUnit 계열 프레임워크의 describe/it 구문이나 계약 테스트·인수 기준을 쓰는 방식에 깊이 뿌리를 남기고 있다.
현장에서의 실무적인 타협점은 대체로 다음과 같이 정리할 수 있다.
- 인수 기준·E2E 수준의 소수의 중요한 시나리오만 Gherkin으로 쓰고, 비즈니스 측과의 합의 형성 도구로 사용한다
- 단위 테스트 수준에서는 자연어 DSL을 쓰지 않고,
describe/it의 어휘만 빌려 가독성을 높인다 - Example Mapping 같은 Discovery 워크숍을 Gherkin 자동화와 분리해, 「요구를 명확히 하는 대화의 도구」로 단독으로 사용한다
이 「협업의 기법」과 「자동화의 기법」을 나누어 파악하는 관점이야말로, 2006년 노스의 문제의식 — 「테스트」라는 말이 일으키는 혼란을 「행위」라는 어휘로 풀어낸다 — 로 돌아가는 형태로, BDD의 현재 위치를 가장 잘 설명해 준다.
이 글에서 다음 글로
BDD가 「행위」라는 어휘로 해결하려 한 것은 어디까지나 무엇을 검증할 것인가, 어떻게 기술할 것인가라는 문제였다. 하지만 테스트 대상이 의존하는 다른 객체를 어떻게 다룰 것인가라는, classicist/mockist 논쟁(앞 장 참조)의 중심에 있던 물음은 여전히 남는다. 다음 장에서는 테스트 더블(스텁·목·페이크·스파이)의 분류와, 목이라는 기법 자체가 어떻게 발전해 왔는지를 따라간다.