JUnit5 테스트 코드 작성해보자 (+BDD)


JUnit 관련 포스팅 참고




테스트 코드를 작성하려다 BDDMockito 라이브러리에 대해 알게 되었고

라이브러리를 활용하여 테스트를 작성하는 법을 알아봤다.

먼저 개발하면서 자주 듣는 TDD와 BDD가 뭔지 알아보자.



TDD 란?

TDD (Test Driven Development) 는 테스트가 개발을 주도한다는 의미이다.

테스트를 먼저 만들고 통과하는 기능을 개발한다.



BDD 란?

BDD (Behavior Driven Development) 는 Danial Terhorst-North와 Charis Matts가 착안한

BDD Introducing - Dan north & associates 방법론으로 BDD의 모든 근간은 TDD에서 착안되었기 때문에

TDD가 추구하는 가치와 크게 다르지 않다.


Danial Terhorst-North는 TDD를 작성하다가 코드 분석과 복잡성으로 인해

누군가 나에게 “이 코드는 ~하게 짜여진 코드야” 라고 말해줬으면 좋겠다고 생각했다고 한다.


그래서 행동 중심 개발, 행동에 기반하여 TDD를 수행하기로 생각한 것이다.

간단하게 말하면 BDD는 애플리케이션이 어떻게 행동해야 하는지에 대한 공통된 이해를 구성하는 방법이다.


TDD, BDD 많은 내용이 있지만 테스트 작성을 위해 BDDMockito 라이브러리 사용법이 대해 자세히 알아보자.



BDD 행동

(1) Narrative (논리적인 일련의 사건)

(2) Given/When/Then


모든 테스트 문장은 Narrative 하게 되어야 한다.

즉 코드보다 인간의 언어와 유사하게 구성되어야 한다.


모든 테스트 문장은 Given/When/Then 으로 나눠서 작성할 수 있어야 한다.





어떤 상태에서 출발 (given)하여 어떤 상태의 변화를 가했을 때 (when)

기대하는 어떤 상태가 되어야 한다.



BDDMockito를 이용한 BDD

주석을 이용하여 Given/When/Then 구조를 나눈 테스트 코드를 많이 봤는데

BDDMockito를 사용하는 방법도 있다.


BDDMockito란 Mockito 라이브러리 내에 존재하는 BDDMockito 클래스를 말하며

BDD 지향적인 개발을 mockito에서 API 형태로 제공한다.




Mockito vs. BDDMockito

순수 Mockito에서 BDD의 Given/When/Then을 위해서

다음과 같이 When() 메서드와 thenReturn()을 이용하고 verify() 구문을 이용해 검증한다.


LinkedList mockedList = mock(LinkedList.class);

// stubbing
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());

System.out.println(mockedList.get(0)); // first
System.out.println(mockedList.get(1)); // Runtime exception 발생
System.out.println(mockedList.get(10)); // null

verify(mockedList).get(0);


위와 같이 특정 상황에 대한(when) 가짜로 결과를 만들어 주는 것을 Stubbing(스터빙)이라 한다.

가짜로 수행할 객체를 넣어주는 것이다.


일련의 BDD의 Stubbing 과정인데 뭔가 맞지 않는 부분이 있다.


위 코드에 존재하는 3가지 행동 과정인데 개념적으로 Given에 해당하는 Mockito의 when(mockedList.get(0))의 이름이 when이라 헷갈린다.




BDDMockito의 given() 메서드

위와 같이 헷갈리는 문제를 given()으로

더 정확한 의미를 전달하는 것으로 해결할 수 있다.

given(someClass.method(any())).willReturn()


given() 메서드

given()은 BDD의 given을 내포하고 있다.

given()의 파라미터를 이용해서 어떤 상황에,

즉 어떤 메서드가 실행되었을 때의 테스트를 위한 상황 설정이 가능하다.


위의 Stubbing 문장에서 3가지 의미를 담고 있다.




Mocking 할 메소드

Mocking 할 메소드라는 말이 무슨 뜻일까?

UserService를 테스트한다고 가정해보자.


Unit Test에서 가장 중요한 것은 테스트 하려는 대상의 고립이다.

테스트 대상을 고립한다는 것은 테스트 대상에 연관된 다른 객체들은 관여하지 않도록

개발자가 가짜 객체를 넣어줘야 한다는 소리와 비슷하다.


테스트 대상을 고립하기 위해서 Mockito의 mock()을 이용했고,

테스트 대상이 특정 결과를 수행하기 위해 연관된 객체의 연상을 주입해주면 된다.


ex) UserService에는 현재 isExistByEmail 이라는 메서드가 존재한다.

해당 메서드 내부에 UserRepository 인스턴스가 existsByEmail 연산을 수행하고 있으므로

개발자는 UserRepository의 existsByEmail을 가짜로 주입해주면 된다.




해당 메서드의 파라미터

해당 메서드는 예제에서 existsByEmail 메서드를 의미한다.

String으로 email을 받는 메서드다.


다음과 같은 선택지가 존재한다.



해당 메서드 수행했을 때 반환하는 값

행동을 반환할 때, 크게 3가지 방법이 존재한다.





참고