프로그래밍 관련/언어들의 코딩들 C++ JAVA C# 등..

자바 단위테스트 JUnit 이걸로 테스트 관련

AlrepondTech 2016. 5. 16. 17:33
반응형

 

 

 

 

=================================

=================================

=================================

 

 

 

출처: http://blog.woniper.net/271

 

junit을 java에서 사용하기 위해서는 2가지 라이브러리가 필요하다.

junit.jar와 hamcrest-core.jar가 필요하다. 다운은 https://github.com/junit-team/junit/wiki/Download-and-Install 여기서 받을 수 있고, junit 공식 사이트는 http://junit.org/

 

junit 사용법을 설명하기 전에 Printer 예제를 만들어보았다. 

 

 

    •  

Printer 예제는 git소스를 참고하고 설명은 하지 않겠다. 사실 junit 사용법만 익히기 위해서 Printer 예제는 볼 필요없다. 아래 설명한 어노테이션만 잘 숙지하면 된다.

    •  

아래는 Printer를 테스트하는 테스트 코드이다.

      • @Before : @Test 메소드가 있는 클래스는 @Test 메소드가 실행 될 때마다 객체가 생성되고 실행된다. 쉽게 말해서 @Test 메소드가 2개라면 2번 실행하기 위해 객체 생성을 2번한다는 말이다. @Before 메소드는 @Test 메소드가 실행되기 전에 반드시 실행되게한다. @Test 메소드가 마다 공통으로 사용하는 데이터를 @Before 메소드에서 실행하는 것인데, 여기서 공통으로 사용하는 데이터를 fixture라고 한다.
      • @BeforeClass : @Before에서 설명 했듯이 @Test 메소드 실행 할 때마다 객체를 생성한다고 설명했는데, 이때 생성되는 객체가 최초에 한번만 실행되는 어노테이션이다.
      • @After : @Test 메소드가 실행된 후 실행된다. 
      • @AfterClass : 객체가 최초 한번 생성 된 후 실행된다.
      • @Test : @Test 메소드는 테스트를 수행하는 메소드다. 아래 예제에 expected가 붙은 @Test가 있는데 이는 예외 상황을 테스트 하기 위한 기능이다.
      • @Ignore : 테스트를 실행하지 않게 한다. @Test가 붙어 있지만, @Ignore가 붙어 있는 메소드라면 테스트 대상 메소드가 아니다.
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      package net.woniper.se.junit.test;
       
      import net.woniper.se.junit.BlackPrint;
      import net.woniper.se.junit.NotSupportImagePrint;
      import net.woniper.se.junit.Printer;
      import org.junit.*;
       
      public class PrintTest {
       
          Printer printer;
       
          @Before
          public void setUp() throws Exception {
              System.out.println("setUp");
              printer = new Printer();
          }
       
          @BeforeClass
          public static void beforeClass() {
              System.out.println("beforeClass");
          }
       
          @Test
          public void testColorPrint() throws Exception {
              printer.btnPrint();
              Assert.assertEquals("COLOR", printer.getKind());
          }
       
          @Test
          public void testBlackPrint() throws Exception {
              printer.setPrint(new BlackPrint());
              Assert.assertEquals("BLACK", printer.getKind());
          }
       
          @Test
          @Ignore
          public void testIgnore() throws Exception {
              System.out.println("testIgnore");
          }
       
          @Test(expected = NotSupportImagePrint.class)
          public void testImagePrint() throws Exception {
              printer.setPrint(new BlackPrint());
              printer.imagePrint();
          }
       
          @After
          public void after() {
              System.out.println("after");
              printer.off();
          }
       
          @AfterClass
          public static void afterClass() {
              System.out.println("afterClass");
          }
      }

 

  •  

 

 

=================================

=================================

=================================

 

 

 

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

 

번 글에서는 JUnit 단위 테스트 Framework에 대해 알아봅니다. JUnit은 Java의 단위 테스트에서 빼놓고 이야기하기 어려울 정도로 절대적인 위치를 차지하고 있습니다. 그래서 높은 수준은 아니어도 기본적인 내용은 누구나 알고 있어야 합니다. 이번 글에서는 Eclipse에서 JUnit을 설치하는 방법과 간단한 예제를 통해 JUnit 사용법을 알아봅니다.

 

1. JUnit이란?

JUnit은 단위 테스트 도구입니다. 외부 테스트 프로그램(케이스)을 작성하여 System.out으로 번거롭게 디버깅하지 않아도 됩니다. 프로그램 테스트 시 걸릴 시간도 관리할 수 있게 해주며 오픈 소스이며, 플러그인 형태로 Eclipse에 포함되어 있습니다.  하나의 jar 파일이 전부이며 사용법도 간단합니다. 어느 정도 개발이 진행되면 프로그램에 대한 단위 테스트는 반드시 수행해야 합니다. JUnit은 보이지 않고 숨겨진 단위 테스트를 끌어내어 정형화시켜 단위 테스트를 쉽게 해주는 테스트용 Framework 입니다. JDK 1.4에서 추가된 assertXXX를 사용하여 Test를 진행합니다. JUnit은 테스트 결과를 확인하는 것 이외 최적화된 코드를 유추해내는 기능도 제공합니다. 또한, 테스트 결과를 단순한 텍스트로 남기는 것이 아니라 Test클래스로 남깁니다. 그래서 개발자에게 테스트 방법 및 클래스의 History를 넘겨줄 수도 있습니다.

 

2. 특징

  • 단위 테스트 Framework 중 하나
  • 문자 혹은 GUI 기반으로 실행됨
  • 단정문으로 테스트 케이스의 수행 결과를 판별함(assertEquals(예상 값, 실제 값))
  • 어노테이션으로 간결하게 지원함
  • 결과는 성공(녹색), 실패(붉은색) 중 하나로 표시

 

3. Eclipse에 JUnit 설정하기

  • Java Project를 생성
  • Project 이름에서 오른쪽 마우스를 클릭하고 Properties를 선택
  • Java BuildPath를 선택
  • Libraries 탭을 선택하고, Add Library를 선택

 


< 그림 1 >

 

JUnit을 선택하고, Next 버튼 선택

 


< 그림 2 >

 

버전을 선택하고, Finish 버튼 선택

 


< 그림 3 >

 

JUnit이 추가 된 사항을 확인 할 수 있습니다.

 


< 그림 4 >

 

그림 4까지 하셨으면 Eclipse에 JUnit이 설정되었습니다. 지금부터는 간단한 Calculator 클래스를 통해 Eclipse에서 어떻게 JUnit을 사용하는지 알아봅니다.

 

4. JUnit 사용하기

먼저 com.calculator 패키지를 만들고 Calculator 클래스를 만듭니다. 메소드는 int형 두 개의 파라미터를 받아 두 개의 합을 리턴 하는 간단한 sum 메소드 입니다. 소스는 다음과 같습니다.

 

Calculator 클래스를 테스트하기 위한 테스트 클래스를 만들겠습니다. 먼저 com. Calculator.test 패키지를 만듭니다. 그리고 com. Calculator.test 패키지에서 마우스 오른쪽 버튼 -> New -> JUnit Test Case 선택

 


< 그림 5 >

 

아래 그림 6에서

①은 테스트 클래스 이름 입력 부분이고, 테스트 클래스 이름은 테스트할 클래스 이름에 Test를 추가해서 입력합니다.

②는 테스트 클래스가 테스트하게 될 클래스를 지정하는 부분입니다.

Next 버튼 선택,

 


< 그림 6 >

 

Calculator 클래스에서 테스트할 메소드를 선택하고 Finish 버튼을 선택,

 


< 그림 7 >

 

다음과 같은 테스트 코드가 생성됩니다.

위 testSum 메소드를 보면 어노테이션으로 @Test가 선언된 것을 볼 수 있습니다. 이 의미는 testSum 메소드가 단위 테스트 메소드임을 지정한다는 것입니다. 그리고 이 testSum 메소드 안에서 Calculator 클래스의 Sum메소드를 테스트합니다.

testSum 메소드를 다음과 같이 수정하였습니다.

위 소스를 보시면 Calculator 클래스 객체를 생성한 후 assertEquals 메소드를 사용하여 sum 메소드의 리턴 값이 제대로 반환되는지 확인합니다. 반환 값이 지정한 결과와 같으면 테스트는 성공입니다. 하지만 반환 값이 생각한 결과와 같지 않으면 실패입니다.

 

테스트 클래스를 실행하는 방법은 테스트 클래스에서 마우스 오른쪽 버튼 -> RunAs -> JUnit Test를 선택

 


< 그림 8 >

 

테스트 메소드가 아무런 문제 없이 성공되면 그림 9와 같습니다.

 


< 그림 9 >

 

테스트 메소드에 문제가 있어 실패하면 그림 10과 같습니다.

 


< 그림 10 >

 

지금까지 간단한 Calculator 클래스를 통해 JUnit에 대해 알아보았습니다. 예제 소스에서 assertEquals 메소드를 보셨을 겁니다. 이 메소드는 JUnit에서 가장 많이 사용되는 메소드입니다. 이외에도 유용한 메소드가 있으며 이러한 메소드를 단정문 이라고도 합니다. 단정문에 해당하는 메소드는 많이 있으며 대표적인 것에 대해 알아보겠습니다.

 

5. 대표적인 단정문

  • 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

 

6. 어노테이션 활용하기

Java 언어 자체가 좀 더 넓게 확장하고 다양하게 발전할 수 있도록 도와준 어노테이션을 JUnit에서 활용할 수 있습니다.

(1) 테스트 메소드 지정하기

@Test가 메소드 위에 선언되면 이 메소드는 테스트 대상 메소드임을 의미합니다.

 

(2) 테스트 메소드 수행시간 제한하기

@Test(timeout=5000)를 메소드 위에 선언합니다. 시간단위는 밀리 초 입니다. 이 테스트 메소드가 결과를 반환하는데 5,000밀리 초를 넘긴다면 이 테스트는 실패입니다.

 

(3) 테스트 메소드 Exception 지정하기

@Test(expected=RuntimeException.class)가 메소드 위에 선언되면 이 테스트 메소드는 RuntimeException이 발생해야 테스트가 성공, 그렇지 않으면 실패입니다.

 

(4) 초기화 및 해제

@BeforeClass, @AfterClass가 메소드 위에 선언되면 해당 테스트 클래스에서 딱 한 번씩만 수행되도록 지정하는 어노테이션 입니다.

@Before, @After가 메소드 위에 선언되면 해당 테스트 클래스 안에 메소드들이 테스트 되기 전과 후에 각각 실행되게 지정하는 어노테이션 입니다.

 

아래 그림 11과 같이 표현할 수 있습니다.

 


< 그림 11 >

@BeforeClass 어노테이션은 테스트 클래스 수행 시 단위 테스트 메소드 보다 먼저 딱 한 번 수행되어야 할 경우 지정합니다. 예를 들어 DB 연결 시 드라이버 로딩 부분을 @BeforeClass로 지정한 메소드에 작성합니다. 반대로 @AfterClass 어노테이션은 단위 테스트 함수들이 다 수행되고 맨 마지막에 수행되어야 할 경우 지정합니다. 예를 들어 DB 연결 후 마지막에 드라이버를 반납하는 부분을 @AfterClass로 지정한 메소드에 작성합니다.

@Before와@After는 @Test로 지정된 단위 테스트 메소드가 실행되기 전 한 번씩 수행하거나 실행되고 난 후 수행되어야 하는 부분을 작성합니다. 예를 들어 공통으로 객체를 생성하는 코드를 @Test로 지정된 단위 테스트 메소드 안에 있다면 그 부분을 @Before에 지정된 메소드에 작성합니다.

 

7. 마치며…

지금까지 단위 테스트 Framework인 JUnit에 대해 알아보았습니다. JUnit을 사용하여 테스트 클래스를 작성하는 데 있어 깊은 내용은 아니지만, Eclipse에서 설치 방법과 어떻게 사용하는지를 살펴 보았습니다. JUnit을 처음 접하시는 분들께 작은 도움이나마 되었으면 하는 바램입니다.

감사합니다.

 

 

 

=================================

=================================

=================================

 

 

출처: http://using.tistory.com/54

 

 

1. JUnit 이란

JUnit은 자바용 단위 테스트 작성을 위한 산업 표준 프레임워크다.

2. JUnit 환경 세팅

JUnit개발 가이드는 이클립스 + springMVC + maven 개발환경 기반으로 작성하였다.
혹 위 환경기반으로 프로젝트를 작성하지 않았다면 아래 개발환경 구축 내용을 확인하기 바람

 

이클립스 + 톰캣 스프링MVC + maven 개발환경 구축 1장
이클립스 + 톰캣 스프링MVC + maven 개발환경 구축 2장
이클립스 + 톰캣 스프링MVC + maven 개발환경 구축 3장

 



2.1 JUnit 라이브러리 추가
JUnit을 사용하려면 프로젝트에 JUnit 라이브러리가 필요하다.
Maven프로젝트는 의존관계 설정이 쉽게 되어 기존 프로젝트에서 처럼 개발자가 해당 라이브러리를 찾는 수고를 덜어준다.
Project Object Model(POM.xml) 에서 아래 그림1과 같이 Dependencies탭에서 JUnit을 찾아 추가를 하면 된다.

 

그림1



또는 직접 POM.xml에 dependencies element에 JUnit dependency를 아래와 같이 직접 추가할 수도 있다.

 

<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.7</version>
  <scope>test</scope>
</dependency>

 



위와 같이 추가를 하게 되면 Maven 프로젝트 Install시 해당 라이브러리가 그림2와 같이 로컬 저장소에 저장하게 된다.

 

그림2

 

그림3

2.2 프로젝트 패키지 구성

JUnit테스트를 하기 위해서는 테스트 대상 클래스와 테스트 클래스는 같은 패키지 내에 있어야 한다. Maven 프로젝트를 생성하게 되면 Maven 관례에 따라, 그림3과 같은 프로젝트 템플릿이 기본적으로 생성된다. 그림3 디렉토리  /src/main/java/는 자바 코드를 보관하고
단위 테스트의 소스는 디렉토리 /src/test/java/ 디렉토리에 보관한다.
테스트 하고자 하는 클래스가 포함된 패키지명과 동일하게 테스트 클래스를 포함하는 패키지도 동일하게 구성한다. 테스트 대상 클래스와 테스트 클래스의 생성 예이다.



3. JUnit 테스트 클래스 작성

3.1 간략한 계산기 클래스 테스트

그림3의 프로젝트 패키지 구성에서 /src/main/java/ 디렉토리에
Calurator.java 클래스를 아래와 같이 작성한다.

 

package com.y2kpooh.junitTest;

public class Calcurator {
    public double sum(double a, doubleb){
        return a + b;
    }
}

 



위 Calcurator클래스는 double형의 두개의 파라메터를 받아 두 파라메터의 합을 구하여 double형으로 리턴해주는 sum 메서드를 가지고 있다.
물론 위 클래스는 문제가 될리 없는 간단한 프로젝트이나 테스트 클래스 작성에 이해를 돕기 위함이다.

Calurator클래스 작성 후 해당 클래스를 테스트 하기 위한 테스트 클래스를 작성해보자.
그림3의 프로젝트 패키지 구성에서 /src/test/java/ 디렉토리에 CaluratorTest.java 클래스를 아래와 같이 작성한다.

 

package com.y2kpooh.junitTest;

import org.junit.Test;
import static org.junit.Assert.*;

public class CaluratorTest {                                                  ←
    @Test                                                                                 
    public void testSum(){                                                        
        Calcurator c = new Calcurator();
        double result = c.sum(10, 50);                                     ←
        assertEquals(60, result, 0);                                           ←
    }
}

 



테스트 클래스는 반드시 public으로 선언해야 하며 클래스명은 관례에 따라 테스트클래명 + Test 끝나는 이름으로 사용된다. JUnit 3에서는 TestCase클래스를 상속받아 사용해야 했으나 JUnit 4에서는 상속받지 않아도 된다. (이 문서는 JUnit 4를 기반으로 작성되었다.)
@Test 어노테이을 선언하여 testSum 메서드가 단위 테스트 메서드임을 선언하였다.
클래스명과 마찬가지로 테스트 메서드는 test + 테스트메서드명으로 선언한다. @Test 어노테이션을 선언한 메서드는 JUnit이 알아서 실행을 해준다.
Calcurator 클래스의 인스턴스를 선언하여 sum 메서드에 10, 50 인자값을 세팅하여 result변수에 결과값을 리턴 받는다.
JUnit 프레임워크에의 Assert 클래스의 정적 메서드인 assertEquals를 이용하여 테스트 결과 값을 확인한다. assertEquals(expected, actual, delta)는 assertEquals(예상값, 실제값, 허용오차)

CalcuratorTest 클래스 테스트 결과 그림4와 같이 테스트 성공 결과가 나온다.


그림4


3.2 JUnit assert 주요 메서드 및 사용예시

 

assert 메서드 설명
assertArrayEquals(a, b); 배열 A와 B가 일치함을 확인한다.
assertEquals(a, b); 객체 A와 B가 일치함을 확인한다.
assertSame(a, b); 객체 A와 B가 같은 객임을 확인한다. assertEquals 메서드는 두 객체의 값이 같은가를 검사는데 반해 assertSame메서드는 두 객체가 동일한가 즉 하나의 객인 가를 확인한다.(== 연산자)
assertTrue(a); 조건 A가 참인가를 확인한다.
assertNotNull(a); 객체 A가 null이 아님을 확인한다.

 


위 메서드 외에도 많은 메서드와 오버로드된 메서드를 제공한다.
자세한 내용은 http://junit.sourceforge.net/javadoc/org/junit/Assert.html 해당 링크를 참고

 

String names[] = {"y2kpooh","hwang"};
String names2[] = {"y2kpooh","hwang"};
assertArrayEquals(names2, names);

List someList = someClass.getSomeList();
assertNotNull("조회결과 null", someList);
assertTrue(someList.size() > 0);
assertEquals(3, someList.size());

 




3.3 JUnit Annotation 사용 예시

스프링 프레임워크 기반의 JUnit 테스트를 위한 세팅

 

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"file:WebContent/WEB-INF/classes/applicationContext*.xml"})

 


Spring 기반의 테스트 코드 작성을 위해 테스트 클래스 상단에 @RunWith(SpringJUnit4ClassRunner.class) 구문을 추가한다.
Spring 프레임워크 context 파일을 테스트 수행시에도 동일하게 로딩하기 위해 @ContextConfiguration(locations={"file:WebContent/WEB-INF/classes/applicationContext*.xml"}) 과 같은 형태로 프로젝트의 스프링 설정파일을 설정해 준다.

메서드 수행시간 제한하기

 

@Test(timeout=5000)

 


단위는 밀리초이며 이 메서드가 결과를 반환하는데 5,000밀리초가 넘긴다면 테스트는 실패한다.

Exception 테스트

 

@Test(expected=RuntimeException.class)

 


해당 클래스는 RuntimeException이 발생해야 한다. 만약 테스트에서 RuntimeException이 발생하지 않을 경우 실패한다.

테스트 건너뛰기

 

@Test(timeout=5000)
@Ignore(value=”여기는 테스트 안할거야”)

 


@Ignore 어노테이션을 추가하면 해당 메서드는 테스트를 건너뛰게 되며 JUnit4는 성공 및 실패 개수와 함께 건너뛴 테스트 수도 포한된 결과 통계를 제공한다.



초기화 및 종료

 

@Before
[...]
@After
[...]

 


@Before 어노테이션이 선언된 메서드는 해당 테스트 클래스의 인스턴스, 객체를 초기하 하는 작업을 한다. @After 어노테이션이 선언된 메서드는 해당 테스트 실행 후 실행된다.
해당 @Before, @After 어노테이션 이외 @BeforeClass, @AfterClass도 있으며 이는 static 메서드와 동일한 형태로 테스트 클래스 실행 시 한번만 실행된다.


4. 목 객체를 활용한 테스트

4.1 목(Mock) 객체란?
 어플리케이션플리케이션을 개발하다보면, 테스트 대상 코드가 다른 클래스에 종속되어 있을 때가 종종 있다. 그 클래스가 다시 다른 클래스에 종속되고, 역시 또 다른 클래스에 종속되기도 한다.
JDBC를 통해 데이터베이스에 의존하는 JAVA EE 애플리케이션, 파일 시스템을 사용하는 어플리케이션, HTTP나 SOAP 등의 프로토콜로 외부 리소스에 접근하는 어플리케이션들을 예로 들 수 있다.
특정 런타임 환경에 의존적인 어플리케이션을 단위 테스트하는 것은 꽤나 고된 일이다.
테스트는 안정적이어야 하고, 반복적으로 수행해도 매번 동일한 결과를 내야 한다. 따라서 테스트를 올바로 수행하기 위해서는 환경을 제어할 수 있어야 한다.
예를 들어 다른 회사에 제공하는 웹 서버에 HTTP 커넥션을 맺는 어플리케이션을 제작하는 경우 그 외부 서버를 개발 환경 안에 가져올 수 없다. 테스트를 작성하고 돌려 보려면, 결국 서버를 시뮬레이션하는 방법이 필요하다.
또는 팀 단위 프로젝트에서 내가 맡은 부분을 테스트해보려 할때 다른 부분이 아직 준비되지 않았다면... 가짜를 만들어 미처 준비되지 못한 부분을 시뮬레이션할 수 있다.
이 처럼 가짜 객체를 제공하는 테스트 방식으로 목 객체를 사용할 수 있다.(스텁방식도 있다.)

4.2 목 객체를 활용해 단위 테스트 하기
한 은행 계좌에서 다른 계좌로 송금하는 단순한 테스트 케이스이다.


위 계좌 송금 프로세스를 기능 테스트 하기 위해서는 AccountService를 테스트 하기 위해서는 우선 데이터베이스를 세팅한 후 테스트 데이터를 채워넣는 작업을 진해하여야 한다.
목 객체를 활용하면 아직 작성되지 않은 코드를 테스트할 수 있다.
단 인터페이스가 정의되어 있어야 한다.

Account.java
계좌 ID와 잔고를 갖는 Account 객체

 

package com.y2kpooh.mock;

public class Account
{
   /**
    * 계좌 아이디
    */
   private String accountId;

   /**
    * 계좌 잔고
    */
   private long balance;

   /**
    * 초기화
    *
    * @param accountId
    * @param initialBalance
    */
   public Account( String accountId, long initialBalance )
   {
       this.accountId = accountId;
       this.balance = initialBalance;
   }

   /**
    * 출금
    *
    * @param amount
    */
   public void debit( long amount )
   {
       this.balance -= amount;
   }

   /**
    * 입금
    *
    * @param amount
    */
   public void credit( long amount )
   {
       this.balance += amount;
   }

   /**
    * 현재 잔고
    *
    * @return
    */
   public long getBalance()
   {
       return this.balance;
   }
}

 



AccountManager.java 인터페이스
Account 객체의 생명주기와 영속성을 관리한다.

 

package com.y2kpooh.mock;

public interface AccountManager
{
   /**
    * 아이디로 계좌 계정찾기
    *
    * @param userId
    * @return
    */
   Account findAccountForUser( String userId );

   /**
    * 계좌 계정 업데이트
    *
    * @param account
    */
   void updateAccount( Account account );
}

 



AccountService.java
두 계정 사이의 송금 기능을 제공한다. ID로 돈을 찾을 계좌와 받을 계좌를 찾고 정보를 갱신하기 위해 앞서 정의한 AccountManager 인터페이스를 활용한다.

 

package com.y2kpooh.mock;

public class AccountService
{
   /**
    * AccountManger 인터페이스 선언
    */
   private AccountManager accountManager;

   /**
    * 객체 초기화
    *
    * @param manager
    */
   public void setAccountManager( AccountManager manager )
   {
       this.accountManager = manager;
   }

   /**
    * 두 계좌 사이 송금기능
    *
    * @param senderId
    * @param beneficiaryId
    * @param amount
    */
   public void transfer( String senderId, String beneficiaryId, long amount )
   {
       Account sender = this.accountManager.findAccountForUser( senderId );
       Account beneficiary = this.accountManager.findAccountForUser( beneficiaryId );

       sender.debit( amount );
       beneficiary.credit( amount );
       this.accountManager.updateAccount( sender );
       this.accountManager.updateAccount( beneficiary );
   }
}

 



MockAccountManager.java
AccountService.transfer 메서드를 단위 테스트하고자 하므로 이를 위해 AccountManger 인터페이스의 목 객체를 구현해야 한다.

 

package com.y2kpooh.mock;

import java.util.Map;
import java.util.HashMap;

public class MockAccountManager implements AccountManager
{

   private Map<String, Account> accounts = new HashMap<String, Account>();

   /**
    * 아이디와 account 객체를 HashMap객체에 put
    *
    * @param userId
    * @param account
    */
   public void addAccount( String userId, Account account )
   {
       this.accounts.put( userId, account );
   }

   /**
    * 아이디로 HashMap객체에서 account 객체를 찾아 리턴
    */
   public Account findAccountForUser( String userId )
   {
       return this.accounts.get( userId );
   }

   /**
    * 계정 정보를 갱신하며 반환값은 없다.
    */
   public void updateAccount( Account account )
   {
       // do nothing
   }
}

 



TestAccountService.java
MockAccountManger를 이용하여 transfer 테스트하기

 

package com.y2kpooh.mock;

import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class TestAccountService
{
   @Test
   public void testTransferOk()
   {
       //테스트를 하기위한 객체 생성 및 준비
       MockAccountManager mockAccountManager = new MockAccountManager();
       Account senderAccount = new Account( "1", 200 );  
       Account beneficiaryAccount = new Account( "2", 100 );
       mockAccountManager.addAccount( "1", senderAccount );
       mockAccountManager.addAccount( "2", beneficiaryAccount );
       
       AccountService accountService = new AccountService();
       accountService.setAccountManager( mockAccountManager );
       // 테스트 수행
       accountService.transfer( "1", "2", 50 );
       // 결과 검증
       assertEquals( 150, senderAccount.getBalance() );
       assertEquals( 150, beneficiaryAccount.getBalance() );
   }
}

 



테스트 결과 및 프로젝트 패키지 구성화면

 

그림5




4.3 목 프레임워크 활용하기
목 객체를 활용하여 테스트 하려면 목 객체를 직접 개발자가 만들어야 한다.
바쁜 프로젝트 일정에 테스트하려고 목 객체를 만들자니 배보다 배꼽이 큰 것 같은 생각이 들지도 모른다. 역시나 천재들이 만들어 놓은 훌륭한 목 프레임워크가 존재 한다.
EasyMock과 JMock이 있으며 해당 라이브러리만 세팅하면 쉽게 목 객체를 활용할 수 있다.

 

<dependency>
  <groupId>org.easymock</groupId>
  <artifactId>easymock</artifactId>
  <version>3.0</version>
</dependency>

 



테스트를 하고자 하는 클래스 AccountService는 이미 완성되어 있으며 해당 클래스를 테스트 하기 위해서는  AccountManager에 대한 Implement Class가 없다. 이 AccountManager에 대한 클래스를 EasyMock을 이용해서 테스트 가능하다.
(4.2 목 객체를 이용한 테스트 케이스에서는 AccountManager의 Implement Class로 MockAccountManager 클래스를 작성하여 테스트가 가능했었다. 여기서 꼭 기억할 것은 목 객체를 이용하여 테스트하기 위해서는 꼭 인터페이스가 정의되어 있어야 한다.)

easymock을 이용한 테스트 클래스 작성

 

package com.y2kpooh.mock;

import static org.junit.Assert.assertEquals;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.verify;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class TestAccountServiceEasyMock
{
   private AccountManager mockAccountManager;

   @Before
   public void setUp()
   {
       //목 객체를 생성한다.
       mockAccountManager = createMock("mockAccountManager", AccountManager.class );
   }

   @Test
   public void testTransferOk()
   {
       Account senderAccount = new Account( "1", 200 );
       Account beneficiaryAccount = new Account( "2", 100 );

       mockAccountManager.updateAccount( senderAccount );
       mockAccountManager.updateAccount( beneficiaryAccount );

       // 기대되는 행위 및 리턴 값 기록 한다.
       // expect : 기대되는 행위 메서드
       // addReturn : 리턴
       expect( mockAccountManager.findAccountForUser( "1" ) ).andReturn( senderAccount );
       expect( mockAccountManager.findAccountForUser( "2" ) ).andReturn( beneficiaryAccount );
       // 해당 목 객체를 수행한다.
       replay( mockAccountManager );

       AccountService accountService = new AccountService();
       accountService.setAccountManager( mockAccountManager);
       accountService.transfer( "1", "2", 50 );

       assertEquals( 150, senderAccount.getBalance() );
       assertEquals( 150, beneficiaryAccount.getBalance() );
   }

   @After
   public void tearDown()
   {
       // 테스트 실행
       verify( mockAccountManager);
   }
}

 

easymock에 대해서 더 자세히 알고 싶으시다면 아래 사이트를 참고하시기 바랍니다.
http://openframework.or.kr/framework_reference/easymock/2.3/Documentation_ko.html


                                                                               그림6

 

 

참고자료 : "JUnit in Action : 단위 테스트의 모든 것"

 

 

 

=================================

=================================

=================================

 

 

 

 

반응형