본문 바로가기

개발새발 개발자/Java

[Java] JUnit을 이용한 TDD 간단 체험하기(진짜 간단함 주의)

티스토리 에디터가 업뎃되고 처음으로 쓰는 글이다. 와우 너무 쾌적해!!!!!!! 기념으로 TDD 글 씁니다. 야호!

 

평소 TDD 단어는 많이 들었으나 정확히 뭔지도 모르겠고 찾아보면 '이게 뭐야 몰라 무서워' 같은 어려운 글밖에 없어서 쉽게 도전하지 못하던 중 내 수준에 맞는(...) 적절한 예제를 찾았다. 왕초보들 팔로팔로미.

 

1. TDD의 개념

Test Driven Development의 약자. 테스트 코드를 먼저 만들고 잘 작동되는지 확인 후에 또 다시 덧붙이고 덧붙이는 방법이다.

검색하면 수많은 고수들이 자세한 설명을 해놓았으므로 그것을 참고하도록 하자. 여기선 '어려운 말은 다 모르겠으니 TDD인지 뭔지 일단 이놈을 뭐라도 경험 해보고싶다' 에서 시작한다!

 

  • 원래 내가 코드를 짜던 방법
일단 하고 싶은 걸 짠다 → 제대로 작동하는지 확인하고 싶다 → System.out.println을 해서 결과 값을 찍어본다 → 혹시나 디버깅할 때 필요할 수 있으므로 주석 처리 한다 → 끝날 때까지 무한 반복한다 → 코드가 주석으로 가득 찬다(....)
일단 하고 싶은 걸 짠다 → 에러가 난다 → 1도 모르겠지만 일단 콘솔창을 들여다본다 → 의심되는 부분에 System.out.println을 해서 하나하나 찍어본다 → 해결될 때까지 무한 반복한다

클래스와 코드 길이가 늘어날 수록 엄청난 노가다였다.

 

  • TDD를 한다면?
일단 아주 소소한 것부터 되는지 확인한다 → success 혹은 fail로 결과가 뜬다 → fail이면 수정을, success면 추가 기능을 덧붙인다 → 다시 성공 여부를 확인한다 → 반복

내가 이 코드를 짰을 때 정상적으로 동작하는지 자동으로 확인해주므로 편리하다. 굳이 하나씩 print를 찍어가며 혼돈의 디버깅을 할 필요가 없다!

2. JUnit으로 TDD 해보기

JUnit(아마도 '제이 유닛'이라고 부르는 것 같다)은 TDD를 Java로 할 수 있도록 도와주는 오픈 소스 도구다. 이클립스에 간단하게 설치할 수 있다.

1) JUnit 설치하기

생성된 Java Project에서 오른쪽 마우스 클릭 → 맨 아래 Properties 클릭

 

왼쪽 메뉴에서 Java Build Path → 오른쪽 영역에서 세번째 탭인 Libraries → Add Library 클릭

 

JUnit 클릭 → Next 버튼

 

원하는 JUnit 버전을 설정 후 Finish

 

2) 기본 클래스 만들기

간단한 계산기 프로그램을 짜보자. 먼저, com.calculator 패키지에 Calculator 클래스를 만든다.

package com.calculator;

public class Calculator {

	 public int sum(int num1, int num2){
	        return num1 + num2;
	    }
        
}

(와...티스토리도 이제 코드 입력된다. 너무 좋다ㅠㅠㅠㅠㅠㅠ)

Calculator 클래스의 내용은 위와 같다. num1과 num2 라는 int형 값을 받아 더하는 sum() 함수이다.

 

3) Test 클래스 만들기

com.calculator.test 패키지 생성 → 오른쪽 마우스 클릭  JUnit Test Case 클릭

 

Name에 CalculatorTest라고 적는다  아래의 Class under test에서 우측의 Browse...를 클릭한다.

 

테스트할 대상을 선택한다. 여기선 Calculator 클래스가 된다.

 

Next를 누른다.

 

해당하는 메소드를 클릭한 후 Finish를 누른다.

 

CalculatorTest 클래스가 생성된다.

 

3) 테스트 코드 넣기

package com.calculator.test;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

class CalculatorTest {

	@Test
	void testSum() {
		fail("Not yet implemented");
	}

}

자동 생성된 Test 코드를 살펴보면 @Test라고 써있는 걸 볼 수 있다. @로 시작하는 것들을 어노테이션이라고 부르는데, Test 어노테이션이 있다는 의미는,

내 아래에 있는 건 Test용 메소드야

라는 뜻이다.

package com.calculator.test;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

class CalculatorTest {

	@Test
	void testSum() {
		Calculator calculator = new Calculator();
		assertEquals(30, calculator.sum(10, 20));
	}

}

위와 같이 testSum 메소드를 수정해보자.

일단, 맨 처음에 미리 만들어놨던 calculator 클래스의 객체를 만들었다. 그런데 아래에 assertEquals라는 메소드가 보인다.

assertEquals(a, b) : 객체 a와 b의 값이 같은지 확인해줘!

위와 같은 메소드를 단정문이라고 하는데 JUnit에서 제공하는 가장 많이 사용하는 메소드다. 내가 만든 함수가 제대로 값을 표현하는지 확인해야하기 때문.

 

즉, 위의 코드를 해석하자면,

내가 calculator 객체에 있는 sum 함수에 10과 20을 넘겼을 때 값이 30이 되는지 확인해줘!

라는 뜻이다.

 

4. Test 코드 실행하기

Run As → JUnit Test로 Test를 실행해보자.

 

짠! 왼쪽에 테스트가 성공했다는 초록색 bar가 떴다. 수행 시간도 같이 계산해준다.

 

만약 틀린 값을 반환한다면 실패가 출력된다.

 

이렇게 내 코드가 제대로 작동하는지 쉽게 확인할 수 있다. println과 같은 노가다는 이제 안녕이다! 이런 식으로 성공 여부를 확인하면서 계속 코드를 늘려나가면 된다.

 

3. JUnit 더 알아보기

1) 단정문

JUnit은 assertEquals 말고도 다양한 단정문을 제공한다.

assertArrayEquals(a,b) : 배열 a와 b의 값 비교
assertEquals(a,b) : 객체 a와 b의 값 비교 
assertSame(a,b) : 객체 a와 b가 같은 객체인지 확인 
assertTrue(a) : a가 참인지 확인 
assertNotNull(a) : a객체가 null이 아님을 확인 

http://junit.sourceforge.net/javadoc/org/junit/Assert.html

 

Assert (JUnit API)

clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

junit.sourceforge.net

여기에 들어가면 더 많은 단정문을 볼 수 있다.

2) 어노테이션

@Test
    public void testSum() { 

    }

앞에서 @Test 는 아래의 메소드가 테스트용임을 나타낸다. 이외에도 다양한 어노테이션이 존재한다.

  • 정해진 시간 내에 끝나는지 테스트 하기
 @Test(timeout=5000)
    public void testSum() { 

    }

이 어노테이션은 연산이 5,000밀리 초를 넘기면 실패라고 말해준다.

  • 런타임 Exception이 발생하는지 테스트 하기
@Test(expected=RuntimeException.class)
    public void testSum() { 

    }

원하는 Exception이 제대로 동작하는지 확인할 수 있다. 만약 JUnit을 사용하지 않았다면 나같은 초보자는 scanner로 잘못된 값을 하나하나 넣어서 확인해봤을 것이다.......ㅎㅎ

  • 메소드가 실행될 때 딱 한번만 수행하도록 지정하기
@BeforeClass
    public static void setUpBeforeClass() throws Exception {

    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {

    }

@BeforeClass는 @Test를 '실행하는 과정에서' 맨 먼저 수행되어야 할 경우를 지정한다.

예) DB 연결 할 때 드라이버 로딩하기

 

@AfterClass는 @Test를 '실행하는 과정에서' 맨 마지막에 수행되어야 할 경우를 지정한다.

예) DB 사용이 다 끝나고 드라이버를 반납할 때

  • 메소드 앞, 뒤로 '독립적인' 수행하도록 지정하기
@Before
    public void setUp() throws Exception {

    }

    @After
    public void tearDown() throws Exception {

    }

@Before는 @Test로 지정된 메소드가 '실행되기 전' 수행된다.

예) 공통으로 생성할 객체가 있는 경우

 

@After는 @Test로 지정된 메소드가 '다 끝난 후' 수행된다.

 

4. TDD의 장단점

1) 장점

  • 처음 해보는 주제라 어떻게 해야할지 모르겠을 때
  • 요구 사항이 매번 바뀔 수 있는 경우
  • 개발 도중에 코드를 많이 바꿀 확률이 있는 경우
  • 내가 개발하고 나서 다른 사람이 유지 보수할 경우

이렇게 불확실성이 큰 경우에는 TDD를 사용할 때 대비할 수 있게 해준다.

 

2) 단점

앞에서 했던 실습을 다시 떠올려보자. 우리는 하나의 클래스에 대해 테스트용의 또 다른 클래스를 만들어줬다. 그리고 작게 쪼개서 일일이 성공하는지 확인하고 추가적으로 코드를 짜야한다.

 

이런 과정은 기존에 하던 방식보다 시간이 더 오래 걸릴 수 있다. 길게 보면 당연히 불안정함을 줄이고 깨끗한 코드를 만들 수 있어 좋지만, 실제 기업에서 일을 할 때는 당장에 있는 일부터 해결해야 하는 경우가 많아 쉽게 적용하지 못하는 것이다.

 

 

참고 자료

http://www.nextree.co.kr/p11104/