ㅤㅤㅤ

JUnit 기본 참고 자료 본문

プログラミング/TDD

JUnit 기본 참고 자료

ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ 2017. 6. 7. 16:51

JUnit Cookbook을 분명히 보고왔건만 맛보기만 해주는 내용이라 cookbook인 것이 확실하다.

직접 eclipse에 JUnit을 도입해서 간단한 테스트를 수행하면

향후 응용에 도움이 될 수 있을 것으로 생각되므로 직접 깔고 테스트를 해보도록 하겠다.




eclipse 개발환경에 JUnit 추가하기


제일 먼저 eclipse를 열어 JUnit library를 추가해보자.

eclipse 조차 깔려있지 않았으므로 그것부터 깔았는데

최근에 나온 eclipse 버전이 다양한 걸 보고 신세계를 경험한 기분...☆


가장 최근에 나온 버전인 MARS의 IDE for Java EE Developers 패키지를 다운받았다.

eclipse를 열어서 새로운 Project를 하나 생성하자.

습관적으로 Dynamic Web Project를 생성하긴 했지만 뭘로 해도 관계는 없을 듯 하다.





프로젝트명은 아주 정직하게도 JUnitExample

프로젝트를 생성하고나서는 해당 프로젝트의 Build Path 설정에 들어가자.


1) 프로젝트 우클릭 -> Properties -> Java Build Path -> Libraries 탭 클릭


또는 Properties를 통해서가 아니라 Build Path로 바로 접근하는 방법도 있다.


2) 프로젝트 우클릭 -> Build Path -> Configure Build Path... -> Libraries 탭 클릭





Libraries 탭에 가서 Add Library 버튼을 클릭하면 추가할 수 있는 Library 목록이 보이는데

그 중간 언저리 즈음에 JUnit이 있다.









location 경로를 보니 eclipse plugin에 내장되어 있는 것으로 확인된다.

없다! 면... JUnit jar 파일을 별도로 다운받으면 된다.


http://junit.org


jar 파일을 다운로드 받아서 classpath 경로에 넣어주거나

maven을 사용하는 경우에는 maven 설정에 junit dependency 값을 넣어주어도 됩니다.






원래 추가된 Library 였던것처럼 자연스럽게 추가되어있는 것을 확인할 수 있다.

이제 JUnit 설치 끝!





매우 간단한 JUnit Test Case 작성하기



그렇다면 각자 만든 Project 내에 JUnit을 테스트 할 내용을 적어보도록 하자.

먼저 테스트를 위한 기존 코드값 작성을 위해 com.junit 이라는 패키지를 만들었다.

(물론 패키지명은 본인들이 원하는 어떤 내용으로 해도 아무런 상관이 없다)



계산식 예제가 가장 간단하므로 Calculator라는 클래스를 선언하고

함수로는 곱셈 함수를 정의하였다. (절대 sum 함수 쓰려다가 찔려서 multiple로 바꾼거 아님)




테스트 할 클래스도 등록했으니 이제 JUnit으로 Unit test를 수행할 Test case를 만들 차례다.

별도로 패키지를 분리하기 위해 com.calculator.test 패키지를 새로 만들었다.

(이 역시도 맘대로 하셔도 됩니다. 아무런 관계가 낫띵이에요.)






JUnit 테스트를 위해 만든 패키지를 우클릭하면 나오는 New를 통해 신규 파일을 만들자.

JUnit은 자주 쓰던 내용이 아니므로 Other쪽에서 찾아야 할 확률이 매우 높다.

검색 기능을 사용하면 바로 툭 튀어나오므로 아래의 캡쳐처럼 JUnit을 찾아보자.





테스트를 위한 기본 단위인 Test case를 만들것이므로 JUnit Test Case를 선택한다.

Test Case를 여러개 만들면 Test Suite를 만들어 한 번에 여러 테스트 수행이 가능하다.


이제 cookbook 내용에 나왔던 내용을 얼추 머리 속으로 짜맞출 수 있다.






package명의 경우 나중에 com.calculator.test로 수정했으므로 캡쳐화면은 무시해주세여...


Name은 Test Case의 클래스명이 되겠다.

여러 블로그를 봤을 때 "기존 클래스명 + Test"의 형태로 많이들 구성하는데

다른 명칭으로 하더라도 제약은 따로 없는 듯 하지만 불안하므로 나도 CalculatorTest를 따랐다.


Class under test에는 Test 대상이 되는 클래스를 지정한다.

여기서는 com.junit에 정의한 Calculator 클래스가 해당된다.






테스트 할 함수를 선택한다.

우리는 multiple 함수만 구현했으므로 선택의 여지가 없네영..


단촐하게 선택하고 Finish 합니다.







기본적으로 생성되는 Test Case 파일 내용이다.

테스트 대상이 되는 Method는 "test + 기존 함수명" 으로 CamelCase 변환되어 생성된다.

@Test Annotation도 아주 잘 붙어있는 것을 확인할 수 있다.


Test Annotation에 파라미터를 부여해서 속성값을 줄 수 있다.

수행시간을 체크할 수 있도록 하는 timeout과 예외 발생여부를 체크할 수 있는 expected가 있다.


사용방법은 JUnit API 링크를 첨부한다.

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





본격적인 테스트 수행을 위해서 구현되지 않은 testMultiple 함수의 내용을 바꿔보도록 하자.

Calculator 클래스에 정의했던 multiple 함수를 호출해서 결과값과 비교하고자 하는 변수값이

같은지를 확인하려고 한다. 값 비교에 이용하는 assertEquals함수는 JUnit에서 제공하는 단정문이다.


단정문은 확실하게 판단하고 결정짓기 위한 함수로, 단정지어진 결론과 다른 경우

false를 반환하는 함수다. 여기서 사용한 assertEquals의 경우는 '같다' 라는 결론을 내려두었고

값을 비교 판단했을 때 같으면 true, 같지 않은 경우 false를 반환하게 된다.


단정문의 종류는 역시 API 링크를 첨부한다.

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






위의 테스트 결과는 false가 나온다.

assertEquals 함수에 매개변수로 넘긴 값이 같지 않기 때문인데

첫 번째 매개변수로 넣은 expected value는 80이고

두 번째 매개변수의 actual value는 Calculator 클래스에 정의한 multiple 함수를 이용하여 7 * 9 = 63의 값을 가진다.


기대되는 값과 실제 값의 결과가 다르기 때문에 fail이 발생하고

Failure Trace에 fail 사유를 설명하고 있다.


해당 테스트의 경우는 예상되어지는 값이 80이라고 적었지만 실제로는 63이므로 실패했다.


assertEquals(63, cal.multiple(7, 9));


로 변경하면 아래와 같이 정상적으로 테스트가 수행되어 초록색 진행바를 만날 수 있다.








Annotation 소개


@Test Annotation이 Test를 수행해야 하는 함수임을 알려주는 것처럼

JUnit에서 사용되는 다른 Annotation이 있어 여기까지 소개하고 끝내도록 하겠다.



@Before

Test 함수보다 먼저 수행되도록 정의하는 Annotation. 

테스트에 필요한 공통적인 내용들을 정의해두면 좋다.

해당 Annotation을 사용하는 함수는 public void로 정의되어야 하며, 상속받은 Superclass 들에

정의된 @Before Annotation을 사용한 함수가 가장 먼저 수행된다.



@After

위에 정의한 Before Annotation과 반대되는 개념으로 Test 함수 이후에 수행된다.

보통 Before Annotation으로 정의된 함수에서 할당한 자원들을 해제하는 용도로 사용됨.

(DB Connection 이 대표적인 예)

이 역시도 public void로 선언하여야 Test 함수 이후에 수행될 수 있음.

Before 또는 Test 함수에서 예외가 발생하더라도 After Annotation이 붙은 함수는 수행된다.

Superclass에서 정의된 After 함수는 현재 클래스에 정의된 After 함수 수행 이후에 수행됨!!


ex) Superclass에 정의한 @Before -> 현재 클래스 @Before -> 

Test -> 현재 클래스 @After -> Superclass에 정의한 @After



@BeforeClass

테스트 수행에서 딱! 한 번 수행되도록 하는 공통 설정이 필요한 경우에 사용하면 좋다.

(예를 들어 Database logging과 같은 수행업무)

테스트의 독립성을 손상시킬 수도 있으나 공통의 내용을 뺄 수 있으므로 효율적인 방법이 되기도 한다.

BeforeClass로 사용될 함수는 public static void 로 선언되어야 하며 매개변수가 있어서는 안된다.

Superclass에 선언된 BeforeClass가 있는 경우에 현재 클래스의 선언내용보다 먼저 수행된다.



@AfterClass

Before와 After 같은 관계로 생각하면 쉽다. BeforeClass에서 할당한 자원 해제에 사용된다.

이 역시도 테스트 수행에서 딱! 한 번만 수행되도록 정의되며 public static void로 선언되어야 한다.

BeforeClass에서 예외를 발생시키더라도 AfterClass는 반드시 수행되며, Superclass에서 정의된 내용은

현재 클래스의 AfterClass가 수행된 이후에 수행된다.



종합해보면...


Superclass에 정의한 @BeforeClass -> 현재 클래스에 정의한 @BeforeClass -> 


Superclass에 정의한 @Before -> 

현재 클래스 @Before -> Test1 -> 현재 클래스 @After ->

Superclass에 정의한 @After ->


Superclass에 정의한 @Before -> 

현재 클래스 @Before -> Test2 -> 현재 클래스 @After ->

Superclass에 정의한 @After ->


현재 클래스에 정의한 @AfterClass -> Superclass에 정의한 @AfterClass



@Ignore

일시적으로 테스트 또는 테스트 그룹을 사용하지 않기를 원할 수도 있다.

@Test가 붙은 함수라고 하더라도 Ignore Annotation이 붙으면 테스트 대상에 포함되지 않고,

또는 Ignore Annotation을 Class에 붙이게 되면 해당 클래스는 테스트 수행되지 않는다.


 

 





Test Case 구성이나 Test Suite 구성에 많은 시간이 소요되겠지만

하나를 잘 만들어두면 두고두고 많은 도움이 될 것으로 보인다.

Annotation이나 단정문이 크게 다양하지 않은 것에 비해 JUnit 자체를 이용하는 사용 파급력은 매우 커보인다.

이제 개발보다 테스트가 더 중요하다고 하니 잘 이용해봐야겠다.




JUnit Assertions

code설명
assertEquals([message], expected, actual)두 값이 같은 지 비교
assertSame([message], expceted, actual)
assertNotSame([message], expceted, actual)
두 객체가 동일한 객체인지 비교
assertTrue([message], expceted)
assertFalse([message], expceted)
참/거짓 판별
assertNull([message], expceted)
assertNotNull([message], expceted)
null여부 판단
fail([message])테스트 실패로 판단

import static org.junit.Assert.*;
으로 static import 하여 쉽게 사용할 수 있음.

JUnit4의 특징

  1. Java5 애노테이션 지원
  2. test라는 글자로 method 이름을 시작해야 한다는 제약 해소
    Test 메소드는 @Test를 붙인다
  3. 좀 더 유연한 픽스처
    @BeforeClass, @AfterClass, @Before, @After
  4. 예외 테스트
    @Test(expected=NumberFormatException.class)
  5. 시간 제한 테스트
    @Test(timeout=2000)
  6. 테스트 무시
    @Ignore(“”)
  7. 배열 지원
    assertArrayEquals([message], expected, actual);
  8. @RunWith(클래스 이름.class)
    JUnit Test 클래스를 실행하기 위한 러너(Runner)를 명시적으로 지정한다.
    @RunWith는 junit.runner.Runner를 구현한 외부 클래스를 인자로 갖는다.
  9. @SuiteClasses(Class[])
    보통 여러 개의 테스트 클래스를 수행하기 위해 쓰인다. @RunWith를 이용해 Suite.class를 러너로 사용한다.
  10. 파라미터를 이용한 테스트 
    @RunWith(Parameterized.class)
    @Parameters
    public static Collection data() {
    }

Junit4 애노테이션
@BeforeClass : 테스트 클래스 내에서 수행 전 한 번만 실행, static method 여야 함
@AfterClass : 테스트 클래스 내에서 수행 후 한 번만 실행, static method 여야 함
@Before : 테스트 케이스 수행 전 반복실행
@After : 테스트 케이스 수행 후 반복실행
@Test : 테스트 메소드 지정

import org.junit.*;

public class Junit4Test {
    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        System.out.println("@BeforeClass");
    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {
        System.out.println("@AfterClass");
    }

    @Before
    public void setUp() throws Exception {
        System.out.println("@Before");
    }

    @After
    public void tearDown() throws Exception {
        System.out.println("@After");
    }

    @Test
    public void testCase1() throws Exception {
        System.out.println("testCase1");
    }

    @Test
    public void testCase2() throws Exception {
        System.out.println("testCase2");
    }
}
import org.junit.Ignore;
import org.junit.Test;

import static org.junit.Assert.*;

public class Junit4Exam {
    // 예외 테스트
    @Test(expected = NumberFormatException.class)
    public void testException() throws Exception {
        String str = "hello";
        System.out.println(Integer.parseInt(str));
    }

    // 테스트 시간 제한
    @Test(timeout = 1000)
    public void testTimeout() throws Exception {
        long sum = 0;
        for (int i = 0; i < 10000; i++) {
            for (int j = 0; j < 10000; j++) {
                sum += j;
            }
        }
        System.out.println(sum);
    }

    // 테스트 무시
    @Ignore
    @Test
    public void testIgnore() throws Exception {
        assertTrue(false);
    }

    // 배열 지원 - 값이랑 순서까지 동일해야 함
   @Test
    public void testAssertArrayEquals() throws Exception {
        Object[] a = {"Java", "Python", 1};
        Object[] b = {"Java", "Python", 1};
        assertArrayEquals(a, b);
    }
}


출처: http://jkonury.tistory.com/196 [마구마구 개발기]





JUnit을 이용한효율적인테스트전략

Table of Contents

테스트란?

  • 자동화된 테스트란? 유형은?
    • 단위테스트 : JUnit
    • 통합/컨테이너 내부 테스트 : Cactus
    • 수락/기능 테스트 : HttpUnit.

Preview

  • 자동화된 테스트란? 유형은?
    • 자동화된 테스팅이란 특히 XP에서 중요시 되는 활동이다.
    • 물론 어느 방법론에 국한될 필요는 없으나
    • 어떤 점에서 잇점이 있는지의 여부의 판단은 중요하다고 생각한다.
    • XP의 중요한 문제는 리팩토링이다.
    • 리팩토링이란 간단하게 설명하면 오브젝트 책임의 명확성과 중복의 제거,
    • 그리고 코드의 간결함 일것이라 생각한다.
    • 자동화된 테스팅은 리팩토링을 하는 프로그래머에 확신을 준다.
    • 그리고 리팩토링은 궁극적으로 테스트를 통해서 완성이 된다.
  • 자동화된 테스트 유형
    • unit test는 가장 많이 언급되는 테스트 유형이나 이것은 전체테스팅의 일부이다.
    • 단위테스트는 통합테스트, 기능 테스트, 기타 테스트와 함께 사용되어 시스템의 동작이 의도에 맞게 동작함을 보증한다.
  • 테스트의 종류 ( 간략하게 정리 )
    • 단위테스트 : JUnit
      • 단위테스트는 단위 코드에서 문제 발생 소지가 있는 모든 부분을 테스트 하는 작업이다.
      • 보통 클래스의 public method 를 테스트 한다.
      • 좋은 단위 테스트란 모든 메서드를 테스트 하는 것이 아니라,
      • 상식 수준의 확인을 통해 단위 코드가 의도한 대로 동작하는지 여부를 판단하는 단계이다.
      • 이상적으로는 코딩전에 테스트 케이스를 작성하여 구현시 보조자료로 활용하는 것이 좋다. ( TDD의 기법 )
      • 단위테스트 후에 개발팀은 테스트를 프로젝트 테스트 스위트에 추가하여 매일 여러번 수행하고 모든 테스트를 항상 통과하게 해야 한다.
      • 기회가 된다면 Code Coverage 를 하는 것이 좋은데
      • 오픈소스로는 JCoverage 등이 있다. - 참고 : j2eestudy자료
    • 통합/컨테이너 내부 테스트 : Cactus
      • 좋은 단위 테스트는 시스템내의 복잡한 부분에 관계없이 클래스 내의 함수들을 검사하는 것이다.
      • 단위 테스트는 가능한 의존성 없이 독립적으로 처리되어야 한다.
      • Mock Object 로 테스트를 하는 경우도 있지만,
      • Cactus 는 J2EE 컨테이너에 접근하는 방법을 제공한다.
      • 컨테이너 안에서 코드 테스트가 가능하도록 하기 위해서 Cactus 는 상세하거나 또는 까다로운
      • 실제와 같은 모형을(mock-ups) 개발자에게 제공한다.
      • 이 방법은 실행되는 코드가 제품이 출시되는 환경에서 실행되기 때문에 또 다른 피드백 기준을 제공한다.
      • 컨테이너 서비스와 상호 작용하는 단일 오브젝트 경우에 컨테이너 내부 테스트를 사용하여 간편한 단위 테스트를 할 수 있다.
    • 수락/기능 테스트 : HttpUnit
      • 기능 테스트는 전체 시스템이 의도한 바대로 동작하는 지를 검사하는 과정이다.
      • 이 방법은 완성된 시스템을 고객으로부터 검사받는 방법이므로 수락 테스트라고도 한다.
      • 기능 테스트는 구조적 기능에 대하여 어떤 프로그램의 기능에 대한 시험이며 진척 상태를 확인하고
      • 이전의 테스트나 누락된 결점을 잡아내거나 미완성 또는 불완전한 부분에서 발생된 문제를 찾아내는 것이 중요하다.
      • 수락 테스트는 고객에 의해 작성된다.
      • 기능 테스트는 항상 100% 구현될 필요는 없으나 제품 출시 전에는 100% 수행 되어야 할것이다.
      • 기능 테스는 종종 매우 구체적인 내용들을 테스트 하기도 한다.
      • 아직은 통괄적인 수락 테스팅 툴은 나오지 않았고 Junit은 어떤 자바클래스에서도 수행될 수 있으나
      • 수락 테스팅 도구는 특정 애플리케이션 요구에 따라 작성되어야 한다.
      • HttpUnit을 이용하면 테스팅 API를 이용하여 웹 리소스에 대한 호출과 응답 값 조회를 프로그래밍 할 수 있도록 한다.
        **부하테스트 : JMeter, JUnitPerf등
        **인수테스트 : .

About Junit

기존에 TDD의 개념은 있었으나 도와주는 도구의 부재로 자바 개발자들은 주로 main() 에서 테스트 하는 방식을 주로 이용했을 것이다.
먼저 JUnit이 무엇이고 테스트란 무엇인지 알아보자.
테스트 코드는 작성시에 일반적으로 권장되는 몇가지 사항이 있다.

  • 이름짓기 규칙

    test 로 시작하는 메서드 이름으로 작성한다.
    즉, createMethod() 라는 메서드를 테스트 하려면 testCreateMethod()로 만드는 것이 좋다.
    그리고 eclipse의 junit 플러그인은 test로 시작된 메서드들을 찾아서 리플렉션을 통해서 테스트를 수행하게 한다.

  • 테스트 코드는 이런 작업들을 수행하도록 설정한다.

    테스트에 필요한 모든 조건과 상황을 준비 설정한다.
    테스트 대상이 되는 메서드를 호출한다.
    테스트 대상이 되는 메서드가 원하는 대로 동작한다는 것을 검증한다.
    실행이 끝나고 다른 코드에 영향이 없게 정리작업을 한다.

  1. JUnit 이란 독립된 테스트를 할 수 있도록 도와주는 framework이다.
  2. JUnit은 웹에서 무료로 다운로드할 수 있다. 설치는 classpath 환경 변수에 추가해주면 완료.
  • 우리는 이클립스 환경에서 사용할 것이므로 별다른 설치 없이 이클립스에서 제공하는 기능을 사용한다.
  • 이클립스의 Junit화면

    Failures 탭은 실패한 테스트의 목록을 나열한다.
    Hierarchy 탭은 실행된 모든 테스트의 전반적인 구조를 보여주는데 이것은 test suite(테스트 묶음) 을 실행할때 특히 유용하다.

  • JUnit의 단정메서드들 ( assert )

    *assertEquals - 같은지 비교
    *assertNull - null값을 리턴하는지 비교
    *assertNotNull - 인자로 넘겨받은 객체가 null인지 판정하고 반대인경우 실패로 처리한다.
    *assertSame - assertSame 은 expected 와 actual이 같은 객체를 참조하는지 판정하고 그렇지 않다면 실패로 처리한다.
    *assertNotSame - expected 와 actual이 서로 '다른' 객체를 참조하는지 판정하고, 만약 같은 객체를 참조한다면 실패로 처리한다.
    *assertTrue - boolean 조건이 참인지 판정한다. 만약 조건이 거짓이라면 실패로 처리한다.
    **assertTrue가 여기저기 들어가 있거나
    **테스트 코드라고 작성한 코드라고 주욱 작성해 놓고 마지막에 이 한줄을 넣어두는 코드는 의미가 없다.
    *fail - 테스트를 바로 실패 처리한다.
    *참고 : JUnit Quick Reference.pdf

  • 이 assert 메서드들은 테스트 대상이 되는 메서드의 검증을 위해 테스트 메서드 하나에는 assert 메서드가 여러개 들어간다.
    assert 메서드가 하나라도 실패하면 테스트 메서드는 중단되고, 나머지 assert 메서드들은 실행되지 않는다.
  • JUnit프레임워크
    1. import문으로 junit클래스들을 참조
    2. 테스트를 포함한 클래스는 모두 TestCase를 상속해야 한다.
    3. 테스트 클래스는 각 test.... 메서드를 정의한다.
    4. 모든 test..... 메서드는 Junit에 의해 자동으로 실행된다.
  • Junit 테스트 조합
    • test suite가 이것을 가능하게 하는데 모든 테스트 클래스는 suite라는 이름의 정적 메서드를 가질 수 있다.
    • public static Test suite();
    • 원하는 테스트 묶음을 반환하는 suite() 메서드를 작성하면 된다.
      ( 이 suite() 메서드가 없으면 JUnit은 모든 test.... 메서드를 자동으로 실행한다. )
  • 테스트별 준비 설정과 정리
    • setUp() 메서드는 각 test..... 메서드들이 실행되기전에 호출된다.
    • tearDown() 메서드는 각각의 테스트 메서드들이 실행되고 난 다음에 호출된다.

      테스트 클래스에 public void setUp() 메소드를 만들어주면 각 테스트 메소드가 실행되기 전에 먼저 setUp() 메소드가 실행된다.
      테스트 클래스에 public void tearDown() 메소드를 만들어주면 각 테스트 메소드가 종료할 때마다 tearDown() 메소드가 실행된다.

// 이 코드는 간단한 프레임워크및 실행순서를 보여준다.
oneTimeSetup(){} 	//- 한번만 실행되는 스위트를 시작할 때의 준비설정코드와 끝날때의 정리코드
    setUp(){} 	//- 메서드별로 테스트 메서드 이전에 실행되는 준비설정코드와, 이후에 실행되는 정리코드
       testMethod(){}
    tearDown(){}
    setUp(){}
	testMethod2(){}
    tearDown(){}
oneTimeTearDown(){}

예를들어, 테스트마다 db연결 객체가 필요하다고 가정할때,
데이터베이스에 연결하고 접속을 종료하는 코드를 각 테스트 메서드에 일일이 넣을 필요 없이
setUp()과 tearDown() 메서드를 이용하여 해결하면 될것이다.

ex)

			 	public Class TestDb extends TestCase(){
			 		private Connection dbConn;
			 		protected void setUp(){
			 			dbConn = new Connection( "oracle", 1521, "scott", "tiger");
			 			dbConn.connect();
			 		}
			 		protected void tearDown(){
			 			dbConn.disconnect();
			 			dbConn = null;
			 		}
			 		public void testEmpAccess(){
			 			//dbConn 사용
			 			어쩌고 저쩌고....
			 		}
			 		public void testDeptAccess(){
			 			//dbConn 사용
			 			어쩌고 저쩌고....
			 		}
  • 이 간단한 예제에서는 setUp이 호출된 다음에 testEmpAccess() 가 호출이 되고 tearDown() 이 호출된다.
  • 그리고 setUp() 이 다시 호출되고 testEmpAccess() 가 실행되고 난 후에 tearDown()이 다시 호출된다.

대개는 이렇게 테스트별 준비 설정만으로 충분하지만
어떤 상황에서는 "전체" 테스트 스위트를 실행하기위해 어떤것을 설정하거나 정리해야 할 필요가 있다.
이런 경우 스위트별 준비 설정과 정리가 필요한데 이 설정은 조금 더 복잡하다.
필요한 테스트들의 스위트를 반들어서 TestSetup 객체 안에 감싸 넣어야 한다.

	 	 public static Test suite() {
		    TestSuite suite = new TestSuite();
		    // Only include short tests
		    suite.addTest(new TestClassTwo("testShortTest"));
		    suite.addTest(new TestClassTwo("testAnotherShortTest"));

		    junit.extensions.TestSetup wrapper =  junit.extensions.TestSetup(suite) {
		    // TestSetup 클래스의 setUp과 tearDown() 메서드를 재정의 한다.
		        protected void setUp() {
		          oneTimeSetUp();
		        }
		        protected void tearDown() {
		          oneTimeTearDown();
		        }
		      };

		    return wrapper;
		  }

		  public static void oneTimeSetUp() {
		    // 한번만 실행되는 초기화 코드가 여기에 들어간다.
		    tsp = new TSP();
		    tsp.loadCities("EasternSeaboard");
		  }

		  public static void oneTimeTearDown() {
		    // one-time cleanup code goes here...
		    tsp.releaseCities();
		  }
  • JUnit에서 제공하는 asssert 메서드만 사용하는 것보다 직접 사용자 정의 클래스를 만들어 상속하여 사용하는 것이 좋다.
  • JUnit과 예외
    1. 테스트에서 발생하는 예상된 예외
    2. 뭔가 크게 잘못 되어서 발생하는, 예상하지 못한 예외
      우리가 일반적으로 생각하는 것과는 달리 예외는 무언가 잘못되었다는 것을 알려주는 굉장히 유용한 도구다.
      가끔 테스트에서는 테스트 대상이 되는 메서드가 예외를 발생시키기를 '바라는' 경우도 있다.
      sortList()라는 메서드를 생각해보자. 이 메서드는 빈 목록을 받았을때 예외를 발생시켜야 한다.
      	public void testForException(){
      		try{
      			sortList(null);
      			fail("Should have trown an exception");
      		} catch (RuntimeException e){
      			assertTrue(ture);
      		}
      	}
      

이 테스트 메서드의 경우 sortList를 테스트 할때 null이 넘어가면 예외를 발생시키는게 맞는 제어흐름이므로
catch절에 assertTrue(ture); 가 수행이 된다.
즉, 이는 나중에라도 코드를 잘못 해석할 가능성을 막아주는 강력한 문서화 기법이다.
다만 assertTrue(ture); 가 호출되지 않는다 하더라도 테스트가 실패하는 것은 아니라는 것은 명심하라.
그렇다면 예상하지 못한 예외들은?
사용자가 직접 처리하는 메서드를 구현할 수 있겠으나
메서드 선언부에 throws 절을 명시해 준다면 JUnit 프레임워크가 발생한 모든 예외를 잡아서 실패한 assert메서드를 출력하며
버그에 이르기까지 "전체" 호출 스택을 출력해 주므로 무엇때문인지 알아볼 때 많은 도움이 된다.

  • Java 의 JUnit은 test...로 시작하는 메서드가 자동으로 테스트 메서드로 인식되어 실행된다.

이것은 ,, 실제 테스트할 준비가 될때까지 메서드의 이름을 test로 시작하는 메서드가 아니라면 실행을 하지 않게 할수 있다는 것을 의미한다.
다만, 익숙해져선 안되는 것은 바로 테스트가 실패하는데도 이를 무시하는 습관이다.

  • JUnit의 테스트 골격 - JUnit테스트 사용에서 정말 필요한것은
    [{ImportantPlugin
  • junit.framework.* 를 반영하기위한 import 문
  • 테스트 클래스가 TestCase를 상속하게 하는 extends 문
  • supert(String)를 호출해주는 생성자
    많은 ide가 최소한 이만큼의 기능은 제공한다.

! 무엇을 테스트 할것인가?

  • 결과가 옳은가? - 당연한 말이다.

    또하나, 많은 양의 테스트 데이타가 필요한 테스트를 한다면, 단위 테스트에서 읽어들이는 별도의 데이타 파일에 테스트 값이나 결과를 넣는 것을 고려하는 것이 좋다.

  • 경계조건 - 대부분의 버그는 보통 '경계'에 서식한다.

    형식일치, 순서, 범위, 참조, 존재성, 개체수, 시간 등을 테스트한다.

    • 형식일치: 이메일, 전화번호, 주민번호등의 간단한 형식부터 레코드에 여러개의 레코드가 연결된 등의 형식 검증
    • 순서 : 정렬등의 루틴 등의 검증
    • 범위 : 애플리케이션 영역의 제약에 따른 범위
    • 물리적인 데이터 구조에 따라 제한되는 범위 - 배열이나 스택등의 인덱스 범위등
    • 참조
      **메서드가 자기 영역을 벗어난 어떤 것들을 참조하는가?
      **외부 의존성이 있는가?
      **클래스가 가져야 하는 상태는?
      **그 메서드가 제대로 동작하려면 그 밖에 어떤 조건을 가져야 하는가?

    예를 들면, 해당 메서드가 동작하기 위한 사전 조건( pre condition ) 이나 사후 조건( post condition ) 은
    어떤 상태여야 하는가? 등의 것들에 대한 것들인데.
    스택의 pop() 메서드처럼 비어있지 않은 스택을 필요로 한다던지
    고객의 거래 내용을 보여주는 어플리케이션에서는 고객의 로그인이 선행 되는 것을 필요로 한다던지
    dbConnection을 끊기 위해서는 먼저 dbConnetion 이 먼저 맺어져 있어야 한다던지 하는 것을

    • 존재성 : 주어진 것이 존재하는가.
      즉, null 이거나 비었거나 0이라면 그 메서드에 어떤 일이 일어날 것인가?
      메서드가 無를 확실히 이겨낼 수 있게 해야 한다.
    • 개체수
      테스트는 경계조건 0,1,n(한개 이상이면 n으로 논리적으로 취급된다.) 에 집중하게 된다.
      n은 업무상 필요에 따라 변하게 될 것이다.
    • 시간
      상대시간(시간적 순서)
      절대시간 ( 경과한 총 계산 시간)
      동시성 문제
  • 역관계 확인 - 논리적 inverse를 적용하여 검증해 볼 수 있다.
  • 다른 수단을 이용한 교차확인
  • 에러조건을 강제로 만들어내기
  • 성능특성

모의객체 사용하기 - 단위 테스트의 목표는 한번에 메서드 하나를 테스트 하는 것이다. 하지만, 테스트 하기가 어려운 상황이라면?

  1. 간단한 스텁 - 설계를 테스트 하는 동안 코드에서 임시로 만들어 사용한다.
    예)
    		public long getTime(){
    				if( debug ){
    					return debug_cur_time;
    				}else{
    					return System.currentTimeMillis();
    				}
    		}
    

    하지만 우리에게 필요한것은 똑같은 일을 해내면서 더 깔끔하고, 더 객체지향 적인 방법이다.

  2. 모의 객체

    진짜 객체가 비결정적인 동작을 한다.( 예상할 수 없는 결과를 만들어 낸다.)
    진짜 객체를 준비 설정하기 어렵다.
    진짜 객체가 직접 유발시키기 어려운 동작을 한다.
    진짜 객체가 느리다.
    진짜 객체가 사용자 인터페이스를 가지거나, 사용자 인터페이스 자체다.
    테스트가 진짜 객체에게 그것이 어떻게 사용되었는지 물어보아야 한다.
    진짜 객체가 아직 존재하지 않는다.( 다른팀이나 새로운 하드웨어 시스템과 함께 일할 때 흔한 문제다.)

    모의 객체를 사용하기 위한 세단계
    *객체를 설명하기 위해 인터페이스를 사용한다.
    *제품 코드에 맞게 그 인터페이스를 구현한다.
    *테스트에 쓸 모의 객체의 인터페이스를 구현한다.

참고자료
http://sourceforge.net/projects/mockobjects
http://www.easymock.org

모의 객체의 사용법의 핵심은 각자가 흉내내는 뭔가에 의존하는 객체를 테스트 하기 위한것이다.

  • 좋은 테스트의 특징
  1. 자동적 : 테스트를 실행하는 경우와 결과를 확인하는 경우를 모두 의미하는 것.
  2. 철저함 - 문제가 될 수 있는 것은 모두 테스트한다.
  3. 반복가능
  4. 독립적
  5. 다른 테스트로부터도 독립적, 환경, 다른 개발자 로부터도 독립적
  6. 전문적 : 전문적 표준을 유지하면서 작성되어야 한다.

    테스트를 테스트 하기위한 방법의 일환으로
    일부러 버그를 집어넣어 테스트를 검증하기도 한다.
    그리고 코드를 고쳐서 테스트를 통과하게 하면 된다.

  • 프로젝트에서 코드의 구조화의 방법
    • 같은 디렉터리
    • 가장 쉬운 방법
    • 단점은 제품 코드와 테스트 코드가 같이 섞여 어질러져 있다는 것
    • 하위 디렉토리
    • 이방법은 테스트 코드를 적어도 제품 코드와 똑같은 디렉터리에 있지는 않게 떨어뜨려 놓을 수 있다는 이점이 있다.
    • test라는 하위의 패키지에 위치하게 되는 테스트 코드를 위해 가시성이 protected 같은 경우, 단지 테스트만을 위한 이 제품코드를 상속바받아서 정보를 노출하는 하위 클래스를 작성해야 한다.
    • 병렬트리
    • test 클래스를 제품 코드와 같은 패키지 안에, 그러나 다른 소스 코드 트리상의 위치에 넣는 것이다.즉, 컴파일러의 classpath안에 두개의 서로 다른 위치를 위치시켜서 인식을 시키는 것이다.
  • 팀작업에서 소스 공유시 테스트 예절은 말 하지 않아도 중요하다는 것에 모두 동의 하실 것이다.

    불완전한 코드(의존성 있는 코드가 체크인 되지 않은.)
    컴파일되지 않은 코드
    컴파일 되긴 하지만, 다른 코드를 망가뜨려서 컴파일 되지 않게 만드는 코드
    대응하는 단위 테스트가 없는 코드
    단위 테스트가 실패하는 코드
    자신의 테스트는 통과하지만, 시스템의 다른 테스트를 실패하게 만드는 코드

  • 테스트빈도의 종류

    새 메서드를 작성할 때마다: 지역단위 테스트들을 컴파일 하고 실행한다.
    버그를 고칠 때마다 : 버그를 드러내는 코드를 실행한다.버그를 고치고 단위테스트를 다시 실행한다.
    성공적으로 컴파일할 때마다 : 지역 단위 테스트들을 실행한다.
    버전 관리 시스템에 체크인할 때마다 : 모든 모듈 또는 시스템의 단위테스트들을 실행한다.
    끊임없이 : 따로 배정된 특정 컴퓨터가 자동으로 하루종일( 주기적으로 , 또는 버전 관리 시스템에 체크인 할때마다)처음부터 전체 빌드와 테스트를 수행하고 있어야 한다.

    Using JUnit Simple Demo

    먼저 테스트 해 볼 것은 간단한 예제로 주어진 배열중 가장 큰수를 리턴하는 메서드를 작성하는 테스트를 작성해 보기로 한다.
    그리고 TestSuite의 사용법을 위해 TestCase를 더 작성후 TestSuite를 작성해본다.

Make TestCases













Make TestSuite






JUnit을 이용한 테스트 전략

전략을 논한다.
현재 내가 알고 있는 지식으로는 어불성설인것 같다.
쉽게 생각하고 접근하기도 너무 넓게 보고 접근하기도 힘든 부분이 없지않아 있다.
Test 를 함에 있어서 자동화된 테스트의 개념이 매우 중요하게 자리잡고 있다.
자동화된 테스트를 위해서는 ANT나 Maven등의 빌드 툴의 사용 또한 익혀야 하며 지속적인 소스통합에 대한 전략과 함께
CodeCoverage 를 할 수 있는 ( 얼마나 빌드했으며 실패율과 성공률을 보기위한 ) 툴이 필수 인듯 싶다.

JUnit과 TestCase, 자동화된 테스트등을 보아오면서 느낀점은 
프로그램을 작성하기전 무엇을 테스트 할것인가와 더불어 어떻게 테스트 할 것인가에 대한 전략을 세우는 것이 가장 어렵고도 중요한 작업이다.
정리는 차차 해 나가기로 하겠다.. **********

맺지 못한 이야기

간단한 게시판 어플리케이션을 JUnit과 Cactus를 이용하여 작성하는 sample

참고서적

논외

*TDD는 다음과 같은 순서로 진행을 하면 된다. 이 순서는 켄트벡이 제시한 순서이다.
1. Quickly add a test(테스트 프로그램을 작성한다.)
2. Run all tests and see the new one fail
(모든 테스트 프로그램을 수행시키고 테스트에 실패한 부분을 확인한다.)
3. Make a little change(소스를 추가하거나 변경한다.)
4. Run all tests and see them all succeed
(다시 모든 테스트를 수행시키고 모두 테스트를 통과했는데 확인한다.)
5. Refactor to remove duplication(중복을 제거하기 위해 Refactoring 한다.)

*이클립스에서 TestCase자동생성






'プログラミング > TDD' 카테고리의 다른 글

Hamcrest matchers list  (0) 2017.06.07
Hamcrest matcher framework tutorial  (0) 2017.06.07
JAVA Project Hamcrest 라이브러리 추가하는 법  (0) 2017.06.07
JUnit Tutorial  (0) 2017.06.07
Comments