행동(Behavior) 중심으로 개발 과정을 이끌어가는 방식입니다.
BDD는 TDD(Test-Driven Development)에서 파생된 개념으로,
사용자 관점에서 시스템의 동작을 정의하고, 이를 개발 및 테스트 과정에 반영합니다.
BDD는 개발자, 테스터, 비즈니스 이해관계자(Stakeholder) 간의 효과적인 소통을 돕고,
이해를 공유하며, 요구사항을 구체화하는 데 중점을 둡니다.
이로 인해 개발팀과 비즈니스 팀 간의 협업이 강화되고,
사용자 요구에 부합하는 소프트웨어를 더 효과적으로 개발할 수 있습니다.

given (혹은 setup) : 테스트 하기 위한 기본 설정 작업 (테스트 환경 구축)when : 테스트할 대상 코드를 실행 (Stimulus)then : 테스트할 대상 코드의 결과를 검증 (이 메소드 범위에선 한 줄 한 줄 자동 assert가 됩니다.)expect : 테스트할 대상 코드를 실행 및 검증 (when + then)where : feature 메소드를 파라미터로 삼아 실행시킵니다.| 항목 | JUnit | Spock |
|---|---|---|
| 철학 | 단위 테스트 중심의 전통적 테스트 프레임워크 | BDD(Behavior-Driven Development) 및 단위 테스트 지원 |
| 언어 기반 | Java | Groovy (JVM 기반의 동적 언어) |
| 테스트 스타일 | 절차적, 명령형 테스트 작성 | 선언적, 가독성 높은 테스트 작성 |
Spock은 Groovy를 기반으로 작성되어서, 이는 Java보다 간결하고 가독성이 높은 문법을 제공해요.
아주 간단한 예제를 살펴볼게요.
@Test
void testAddition() {
// given
int a = 2;
int b = 3;
// when
int result = a + b;
// then
assertEquals(5, result);
}
def "addition works correctly"() {
given:
def a = 2
def b = 3
when:
def result = a + b
then:
result == 5
}
given-when-then 스타일을 제공해 코드의 의도를 더 명확히 표현할 수 있어요.Mocking 및 Stubbing
@Test
void testWithMock() {
MyService service = mock(MyService.class);
when(service.getData()).thenReturn("mocked data"); //mocking
assertEquals("mocked data", service.getData());
}
groovy
코드 복사
def "test with mock"() {
given:
def service = Mock(MyService)
service.getData() >> "mocked data"
expect:
service.getData() == "mocked data"
}
| Spock | JUnit |
|---|---|
| Specification | Test class |
setup() | @Before |
cleanup() | @After |
setupSpec() | @BeforeClass |
cleanupSpec() | @AfterClass |
| Feature | Test |
| Feature method | Test method |
| Data-driven feature | Theory |
| Condition | Assertion |
| Exception condition | @Test(expected=…) |
| Interaction | Mock expectation (e.g. in Mockito) |
build.gradle


위와 같이 groovy를 설정하면 Spock을 사용할 수 있는 환경을 구축할 수 있어요.
✅ 취소 조건 검증
충전성공 상태의 거래건✅ 취소 기한 검증
충전결제일시 로 부터 7일 이내 충전 거래는 취소 가능하다.휴대폰결제의 경우 당월 충전 분에 한해 충전결제일시로 부터 7일 이내에 취소 가능하다.ex) 취소 기한 검증을 위해 충전결제일자를 수기로 업데이트 합니다.
DEVAPP> UPDATE DEVAPP.CHG_PAY t SET t.CHG_PAY_DH = '20241231161257' WHERE t.CHG_PAY_NO LIKE 'BK250106161242110607
[2025-01-07 10:52:41] 1 row affected in 6 ms
DEVAPP> UPDATE DEVAPP.CHG_PAY t SET t.CHG_PAY_DH = '20241231161238' WHERE t.CHG_PAY_NO LIKE 'BK250106161155110605'
[2025-01-07 10:52:41] 1 row affected in 6 ms
DEVAPP> UPDATE DEVAPP._CHG_PAY t SET t.CHG_PAY_DH = '20241231160708' WHERE t.CHG_PAY_NO LIKE 'BK250106160644110603'
💡 물론 통합 테스트를 위해서 위의 명시한 내용은 모두 진행 해야 지만, 단위 테스트를 통해 기능의 정상 동작이 보장된다면 테스트 수행 시간을 줄이고 효율적으로 진행할 수 있는 기대감이 생기지 않을까요?
💡 기존에 테스트 했던 상황들을 생각해보아요.
def "휴대폰 결제의 경우 당월 취소만 충전 취소가 가능하다."() {
when:
def isCancelable = service.isCancelAvailablePhonebillTxn(chgPayDh)
then:
isCancelable == result
where:
chgPayDh || result
"20241231145000" || false
"20251231145000" || false
}
def "충전결제 일자가 현재 일자로부터 7일 이상인지 판단한다."() {
when:
def isCancelable = service.isChgTxnOverSevenDays(chgDay)
then:
isCancelable == result
where:
chgDay || result
"20241230102231" || true
"20241231102231" || true
"20250101102231" || false
}
def "휴대폰 결제의 경우 당월 충전 분에 한하여 7일 이내에 취소 가능하다."() {
given:
bubiCashChargeCancelAvailableTxnMapper.getChargePayMethod(_) >> createPhoneBillChgPayMethod()
bubiCashChargeCancelAvailableTxnMapper.getChgTxnListWithinSevenDays(_) >> createChgCancelPhonebillTxnList()
bubiCashChargeCancelAvailableTxnMapper.getTotalPayAmtWithinSevenDays(_, _, _) >> 2000
when:
def chgCancelAvaliableList = service.getChargeCancelAvailableTxnList(request)
then:
chgCancelAvaliableList.size() == 2;
}
def "결제 완료일로부터 7일이 지난 충전 거래라면 취소가 불가능하다."() {
given:
bubiCashChargeCancelAvailableTxnMapper.getChargePayMethod(_) >> createChargePayMethod()
bubiCashChargeCancelAvailableTxnMapper.getChgTxnListWithinSevenDays(_) >> createChgCancelNotAvailableTxn()
bubiCashChargeCancelAvailableTxnMapper.getTotalPayAmtWithinSevenDays(_, _, _) >> 4000
when:
def chgCancelAvailableList = service.getChargeCancelAvailableTxnList(request)
then:
chgCancelAvailableList.size() == 0
}
_ 는 와일드카드로서, 메서드 호출 시 전달되는 특정 파라미터 값을 무시하거나 어떤 값이 전달 되더라도 매칭되도록 설정할 수 있어요💡 Fixture
- Test Fixture는 테스트를 수행하기 전에 필요한 상태나 환경을 설정하는 것을 의미합니다. 예를 들어, 데이터베이스 연결을 성립하거나 테스트 데이터를 생성하는 등의 작업이 Test Fixture에 해당합니다.
- 이와 같이 간단하게 테스트 하기 위해 필요한 데이터들이 담긴 객체를 생성하여 테스트 대상으로 활용할 수 있어요.
💡 Mocking
주로 객체 지향 프로그래밍으로 개발한 프로그램을 테스트 할 경우 실제의 모듈을 "흉내"내는 "가짜" 모듈을 작성하여 테스트의 효용성을 높이는데 사용하는 객체이다. 사용자 인터페이스(UI)나 데이터베이스 테스트 등과 같이 자동화된 테스트를 수행하기 어려운 때 널리 사용된다.
- Spock에서 Mock 객체의 반환값은
>>로 지정해요.- Mocking을 통해 특정 데이터의 반환 값을 가정하고 해당 데이터가 반환 되는 경우 어떻게 작동하는지 테스트 해볼 수 있어요.
https://jojoldu.tistory.com/228
https://techblog.woowahan.com/2560/
https://spockframework.org/spock/docs/1.0/spock_primer.html