Development note/Java

[Java] Eclipse에서 Junit을 사용하는 방법

v명월v 2020. 6. 10. 13:35

안녕하세요. 명월입니다.


이 글은 Eclipse에서 Java의 Junit을 사용하는 방법에 대한 글입니다.


Junit이란 Java 프로젝트를 할 때, 단위(Unit) 테스트하는 Framework입니다. 우리가 함수를 만들 때나 클래스를 만들 때, 그 만든 목적이 있습니다.

예를 들면 계산기(Calculator)클래스가 있고 더하기 함수(add)와 빼기 함수(subtract), 곱하기(multiply), 나누기(division) 함수가 있습니다.

계산기 클래스 안에는 현재의 데이터를 보존하는 data 변수가 있고 최종 값을 받을 수 있는 함수가 있습니다.


여기서 Junit으로 각 함수가 제대로 작동하는지, data 변수가 제대로 값이 계산이 되는지 최종 값은 문제없이 리턴이 되는지 확인할 수 있습니다.

그럼 먼저 Junit를 사용하기 위해서는 이클립스에서 몇가지 설정이 필요합니다.


기본적으로 이클립스에서 프로젝트를 시작하면 아래처럼 src 폴더만 있는 경우가 있습니다.

그럼 먼저 이 폴더 세팅부터 변경하겠습니다.

프로젝트에 마우스 오른쪽 클릭을 해서 Build path를 수정합니다.

그리고 폴더를 아래와 같이 세팅합니다.

폴더는 src/main/java와 src/main/resources, src/test/java와 src/test/resources를 생성합니다.

src/main/java는 메인 소스 파일을 두는 폴더입니다.

src/main/resources는 리소스 파일을 두는 폴더입니다.

src/test/java는 테스트 소스 파일을 두는 폴더입니다.

src/test/resources는 테스트용 리소스 파일을 두는 폴더입니다.

그리고 소스 폴더로 가서 라이브러리를 추가합니다.

Junit5도 있습니다만 아직 라이브러리가 4까지 밖에 없습니다. Junit5는 어디서 구하는 거지??

추가하고 등록을 누릅니다.

그리고 이제 프로젝트에 maven 기능을 추가합니다.

그리고 junit 라이브러리를 업데이트합니다.

레포지토리 - https://mvnrepository.com/artifact/junit/junit

<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.13</version>
  <scope>test</scope>
</dependency>

이제 테스트를 위해서 java의 계산기 프로그램을 작성하겠습니다.

public class Calculator {
  // 맴버 변수 (현재 데이터)
  private int data = 0;
  // 더하기 함수
  public void add(int data) {
    // 맴버 변수에 값을 더한다.
    this.data += data;
  }
  // 빼기 함수
  public void subtract(int data) {
    // 맴버 변수에 값을 뺀다.
    this.data -= data;
  }
  // 곱하기 함수
  public void multiply(int data) {
    // 맴버 변수에 값을 곱한다.
    this.data *= data;
  }
  // 나누기 함수
  public void division(int data) {
    // 맴버 변수에 값을 나눈다.
    this.data /= data;
  }
  // 현재 계산 결과
  public int getData() {
    // 맴버 변수 리턴
    return this.data;
  }
  // 맴버 변수 클리어
  public void clear() {
    // 맴버 변수 0 설정
    this.data = 0;
  }
}

이제 테스트 클래스를 작성하겠습니다.

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.lang.reflect.Field;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import junit.framework.AssertionFailedError;
public class CalculatorTest {
  // 테스트할 클래스
  private Calculator calculator = null;
  // 클래스 내부 필드 Reflection
  private Field dataField = null;
  // CalculatorTest가 생성되기 전에 호출되는 함수
  @BeforeClass
  public static void init() throws Throwable {
    // 콘솔 출력
    System.out.println("@BeforeClass");
  }
  // CalculatorTest의 테스트가 끝나면 호출되는 함수
  @AfterClass
  public static void destory() {
    // 콘솔 출력
    System.out.println("@AfterClass");
  }
  // 각 @test의 함수가 실행되기 전에 호출되는 함수
  @Before
  public void before() throws Throwable {
    // 콘솔 출력
    System.out.println("@Before");
    // 맴버 변수 calculator 인스턴스가 없으면
    if (calculator == null) {
      // 인스턴스 생성
      calculator = new Calculator();
    }
    // 맴버 변수 dataField에 데이터가 없으면
    if (dataField == null) {
      // 필드 취득
      dataField = Calculator.class.getDeclaredField("data");
      // private 해제
      dataField.setAccessible(true);
    }
    // 초기 설정 0
    setFieldData(0);
  }
  // @test 함수가 끝나면 호출되는 함수
  @After
  public void after() throws Throwable {
    System.out.println("@After");
    setFieldData(0);
  }
  // 클래스의 data 멤버 변수 값을 설정
  private void setFieldData(int data) throws Throwable {
    // Reflection을 이용해서 데이터 설정
    dataField.set(calculator, data);
  }
  // 클래스의 data 맴버 변수 값 취득
  private int getFieldData() throws Throwable {
    // Reflection을 이용해서 데이터 값 취득 후 리턴
    return (int) dataField.get(calculator);
  }
  // add 함수 테스트
  @Test
  public void add() throws Throwable {
    // 콘솔 출력
    System.out.println("@Test - add");
    // calculator 인스턴스의 add 함수를 통해 10을 넣는다.
    calculator.add(10);
    // 값 확인
    assertEquals(getFieldData(), 10);
    // 맴버 변수 값이 10이 아니면
    if (getFieldData() != 10) {
      // 에러
      Assert.fail("add not equals");
      // 에러 발생
      throw new AssertionFailedError("add not equals");
    }
  }
  // subtract 함수 테스트
  @Test
  public void subtract() throws Throwable {
    // 콘솔 출력
    System.out.println("@Test - subtract");
    // calculator 인스턴스의 subtract 함수를 통해 10을 넣는다.
    calculator.subtract(10);
    // 값 확인
    assertEquals(getFieldData(), -10);
  }
  // multiply 함수 테스트
  @Test
  public void multiply() throws Throwable {
    // 콘솔 출력
    System.out.println("@Test - multiply");
    // calculator 인스턴스의 멤버 변수에 10을 넣는다.
    setFieldData(10);
    // calculator 인스턴스의 multiply 함수를 통해 10을 넣는다.
    calculator.multiply(10);
    // 값 확인
    assertEquals(getFieldData(), 100);
  }
  // division 함수 테스트
  @Test
  public void division() throws Throwable {
    // 콘솔 출력
    System.out.println("@Test - division");
    // calculator 인스턴스의 멤버 변수에 10을 넣는다.
    setFieldData(10);
    try {
      // calculator 인스턴스의 division 함수를 통해 0을 넣는다.
      calculator.division(0);
      // 반대로 에러가 발생하지 않으면 에러다...
      Assert.fail("division do not run the exception. / by zero");
    } catch (ArithmeticException e) {
      // 에러가 발생하면 OK!
    }
    // calculator 인스턴스의 멤버 변수에 10을 넣는다.
    setFieldData(10);
    // calculator 인스턴스의 multiply 함수를 통해 5을 넣는다.
    calculator.division(5);
    // 값 확인
    assertEquals(getFieldData(), 2);
  }
  // getData 함수 테스트
  @Test
  public void getData() throws Throwable {
    // 콘솔 출력
    System.out.println("@Test - getData");
    // calculator 인스턴스의 멤버 변수에 10을 넣는다.
    setFieldData(10);
    // 값 확인
    assertEquals(calculator.getData(), 10);
  }
  // clear 함수 테스트
  @Test
  public void clear() throws Throwable {
    // 콘솔 출력
    System.out.println("@Test - clear");
    // calculator 인스턴스의 멤버 변수에 10을 넣는다.
    setFieldData(10);
    // calculator 인스턴스의 clear 함수 호출
    calculator.clear();
    // 값 확인
    assertEquals(getFieldData(), 0);
  }
}

Junit은 따로 Debug를 하는 건 아니고 프로젝트나 파일를 클릭하고 오른쪽 마우스 클릭을 하면 Junit 테스트 항목이 나옵니다.

Junit에서 일단 Exception이 발생하면 Test가 실패로 생각합니다.

위 add함수를 보면 assertEquals를 사용해서 데이터를 비교했습니다.

그런데 꼭 assertEquals를 사용할 필요는 없고 에러만 발생하면 되니깐 throw 로 에러를 발생시키면 됩니다.

Junit에서 값이 틀릴 경우에는 보통 AssertionFailedError Exception이 발생합니다.


그리고 Assert.fail의 경우도 내부에서는 exception를 발생합니다.

결국 다 exception이 발생하면 Junit에서는 fail로 인지하기 때문에 꼭 assertEquals를 사용할 필요는 없습니다.

그러나 스탭 크기상 assertEquals나 assertTrue, assertNotNull, assertArrayEquals등을 사용하는 것도 나쁘지 않습니다.


그리고 저는 Reflection을 통해서 클래스의 변수를 취득했습니다.

링크 - [Java] 31. Reflection 기능을 사용하는 방법 - Variable편


위 소스에서는 getData로 데이터를 취득이 가능합니다만 그렇게 하면 나중에 getData 함수가 수정이 되었을 때, 다른 Test에도 영향이 가기 때문에 Reflection으로 직접 데이터를 확인하는 방법이 좋습니다.

또 확인할 것은 division 함수에서 0을 넣었을 때, 에러가 발생하는 지 안하는지 확인도 해야합니다.

오히려 에러가 발생하지 않으면 버그이기 때문에 try에 Assert.fail를 넣었습니다.


여기까지 Eclipse에서 Java의 Junit을 사용하는 방법에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.