[SpringBoot] TDD를 도입해보자
이동욱님의 '스프링 부트와 AWS로 혼자 구현하는 웹 서비스' 책과 채수원님의 'TDD 실천법과 도구'를 참고했다.
코살람 유지보수를 다시 시작해볼까한다.
유지보수 때는 TDD 개발 방식을 도입해보려고 한다.
인턴 기간 중 테스트코드가 오히려 총 개발 시간을 줄여준다는 걸 깨달은 적이 있었다. 신뢰성은 덤~!
마침 코살람 유지보수를 시작하기로 마음먹었으니 여기에 적용해보려고 한다!
적용하고 나면 코드를 수정할 때마다 Tomcat 을 내렸다 켰다 하는 경험을 하지 않을 수 있게 될 것이다. 얏호
✏️ TDD란?
Test Driven Development의 약자. 테스트가 주도하는 개발
1. 항상 실패하는 테스트를 먼저 작성하고(RED)
2. 테스트를 통과하는 프로덕션 코드를 작성하고(Green)
3. 테스트가 통과하면 프로덕현 코드를 리팩토링한다.(Refactor)
조금 더 자세한 설명
1. Ask(질문): 테스트 작성으르 통해 시스템에 질문(결과는 실패로)
2. Respond(응답): 테스트를 통과하는 코드를 작성해서 질문에 대답(성공)
3. Refine(정제): 아이디어를 통합하고 불필요한 것은 제거하고, 모호한 것은 명확히 해서 대답을 정제(리팩토링)
4. Repeat(반복): 다음 질문을 통해 대화를 계속 진행
음 TDD는 테스트 작성을 먼저 한다는 게 가장 중요한 전제 조건인데
코살람은 이미 기능 구현이 돼있다.
TDD를 적용한다기보단 공부해서 테스트 코드를 추가했다...라고 봐야할듯하다.
물론 앞으로 추가되는 기능들은 전부 TDD를 적용할 예정이다.
Unit Test
- 전통적인 테스트 방법론에서 말하는 Unit Test
사용자 측면에서 제품의 기능(function)을 테스트 - TDD에서 말하는 Unit test
메소드 or 함수 단위의 테스트를 뜻함 - 장점
- 초기에 문제 발견 가능
- 회귀 테스트 가능(코드 수정 / 리팩토링 시 기존 기능이 올바르게 작동하는지 확인 가능)
- 기능에 대한 불확실성 감소
- 시스템 문서 기능 역할을 수행할 수 있음
✏️ 사이클 예시
TDD 사이클을 한번 돌아보자!
JUnit 과 같은 단위테스트 프레임워크를 사용하지 않은 예시다.
역시 맨땅부터 찬찬히 공부하는 게 가장 효과적인 방법인 것 같다.
Ask 단계
테스트 작성으르 통해 시스템에 질문(결과는 실패로)
- 테스트 시나리오 작성 : 기도실을 생성한다 -> 정상적으로 생성됐는지 확인한다.
- 실패하는 테스트 메소드 생성
TDD에는 테스트의 최소 작성 단위를 최하위 모듈의 단위와 일치시킨다. Java 언어 기준으로 최하위 모듈은 method다.
첫 ask 단계에서는 메소드 수준의 단위 테스트를 작성한다.
작성하고자 하는 메소드나 기능이 무엇인지 선별하고
작성 완료 조건을 정해서 실패하는 테스트 케이스를 작성하는 것이다.
우선 구현해야하는 기능을 정리해보자
- 클래스 이름은 Prayerroom
- 기능
- CRUD
- 반경 Nkm 내의 기도실 리스트 조회
- 좋아요
- 리뷰
정리한 기능에 대한 테스트 케이스를 하나씩 추가해나가면서 구현 클래스를 점진적으로 만들어나가는 방식이 가장 좋다.
대부분의 경우 하나의 테스트 케이스는 하나의 메소드로 표현된다.
public class PrayerroomTest {
public void testPrayerroom() {
Prayerroom prayerroom = new Prayerroom();
if (prayerroom == null) {
throw new Exception("기도실 생성 실패");
}
}
Respond 단계
테스트를 통과하는 코드를 작성해서 질문에 대답(성공)
- 기도실 생성 테스트 케이스를 통과하는 코드를 작성한다.
public class PrayerroomTest {
public void testAccount() throws Exception{
Account account = new Account();
if ( account == null){
throw new Exception("계좌생성 실패");
}
}
public static void main(String[] args) {
AccountTest test = new AccountTest();
try {
test.testAccount(); // 테스트 케이스 실행
} catch (Exception e) {
System.out.println("실패(X)"); // 예외가 발생하면 실패(X)
return;
}
System.out.println("성공(O)");
}
}
한 가지 짚고 넘어갈 건!
사실 예시로 작성한 객체가 잘 생성되는지에 대한 테스트는 일반적으로 필요없다.
Java JDK가 망가지지 않는 이상.. 문제될 일이 없기 때문이다.
다만, 생성 로직에서 특별한 처리가 필요할 때(ex. 1000이상의 값이 들어가면 안된다. not nullable이다.)는 있는 게 좋다.
테케는 메소드 사용 설명서
라는 측면에서 해당 클래스를 사용하게 될 다른 개발자들에게 도움이 되기 때문이다.
Refine 단계
아이디어를 통합하고 불필요한 것은 제거하고, 모호한 것은 명확히 해서 대답을 정제(리팩토링)
아래와 같은 항목을 리팩토링 적용 대상으로 고민해본다.
- 소스의 가독성이 적절한가?
- 중복 코드는 없는가?
- 메소드명과 변수명이 적절한가?
- 구조 개선이 필요한 부분은 없는가?
✏️ JUnit4 사용하기
JUnit?
에릭 감마와 켄트 벡이 탄생시킨 JUnit은 현재 전세계적으로 가장 널리 사용되는
Java 단위 테스트 프레임워크다.
구성요소
(1) 단정문 (assertion)
테스트 결과가 예상과 같은지 판별하는 것
@Test // 테스트 메소드로 지정해줌
public void testAssertFalse() {
assertEquals([message], expected, result);
assertSame([message], expected, result); // 두 객체가 동일한 객체인지 주소값으로 비교
assertTrue([message], expected);
assertNull([message], expected);
}
assertSame
단정문은 주로 동일 객체임을 증명하는 데 쓰인다. 이를테면 캐시 기능을 만들었는데 해당 캐시가 제대로 동작하는지 판단해야한다고 가정해보자. 이때 특정 객체가 캐시에서 가져온 객체와 동일한지 여부를 판단할 수 있다.
cache.add(someObject, KEY);
assertSame("캐시처리 실패!", someObject, cache.lookup(KEY));
싱글톤(특정 클래스의 인스턴스가 오직 하나만 생성될 수 있도록 하는 디자인 패턴)으로 만들어진 객체를 비교할 때 쓰이기도 한다.
(2) 테스트 러너(Test Runner)
작성한 테스트를 실행하기 위해서는 내장된 JUnit 태스트 러너를 이용하는 방법과 테스트 클래스를 직접 실행하는 방법이 있다.
JUnit 내장 Test Runner
java 프로그램에서 실행
org.junit.runner.JUnitCore.runClasses(TestClass1.class, ...);
콘솔에서 실행
$ java org.junit.runner.JUnitCore TestClass1 [...other test classes...]
@RunWith
annotation
JUnit에 내장된 러너 대신 해당 클래스에서 테스트를 실행하기 위해 참조하는 클래스를 호출한다.
@RunWith(SpringJUnit4ClassRunner.class)
라고 하면
테스트를 진행할 때 JUnit 내장 러너 대신 스프링 부트를 테스트하는 실행자를 실행시킨다고 보면된다. 즉, 스프링 부트 테스트와 JUnit 사이의 연결자 역할을 해준다.
@RunWith(SpringJUnit4ClassRunner.class)
public PrayerroomTest {
@Test
public getPrayerroomTest() {
}
}
(3) test fixture
테스트를 반복적으로 수행할 수 있게 도와주고 매번 동일한 결과를 얻을 수 있게 도와주는
기반이 되는 상태나 환경를 의미한다. 일관된 테스트 실행환경이라고도 함.
For example
- Preparation of input data and setup/creation of fake or mock objects
- Loading a database with a specific, known set of data
- Copying a specific known set of files creating a test fixture will create a set of objects initialized to certain states.
- @BeforeClass
- @AfterClass
- @Before
- @After
이제 개념 공부는 대강 끝났으니 Kosalaam 프로젝트에 단위테스트부터 추가해보려고 한다. 그 과정은 다음 글에서!
Author And Source
이 문제에 관하여([SpringBoot] TDD를 도입해보자), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@dchecheb/SpringBoot-TDD-적용기저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)