Unit Test

oyeon·2021년 1월 15일
0

JUnit x Spring 에서 이어짐

  • Integration test : 하나의 bean을 테스트할 때 관련된 bean들이 모두 잘 동작하는지 테스트
  • Unit test : 관계된 다른 클래스와 상관 없이 특정 bean이 가진 기능이 잘 동작하는지 확인하는 것
  • 인수테스트 : 해당시스템이 실제 운영환경에서 작동하는지 확인, 단위/통합 테스트와 다르게 실제 고객 혹은 사용자가 참여한다. 앞선 2개의 테스트가 '결함'을 찾는것이 목적이라면, 인수테스트는 이제 사용해도 되는지 '확신'을 갖기 위한 테스트이다.

bean과 bean 사이의 관계 맺기

  1. MyService.java 생성
package org.edwith.webbe.calculatorcli;

import org.springframework.stereotype.Service;

@Service
public class MyService {
	// MyService 클래스는 CalculatorSerivce를 필드로 갖는다.
	private final CalculatorService calculatorService;
	
	public MyService(CalculatorService calculatorService) {
		this.calculatorService = calculatorService;
	}
	
	public int execute(int value1, int value2) {
		return calculatorService.plus(value1, value2) * 2;
	}
}
  1. MyServiceTest.java 생성
package org.edwith.webbe.calculatorcli;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/* @RunWith(SpringJUnit4ClassRunner.class)
 * : JUnit이 테스트 코드를 실행할 때 Spring Bean 컨테이너가 내부적으로 생성되도록 한다.
 * 
 * @ContextConfiguration(classes = {ApplicationConfig.class})
 * : 내부적으로 생성된 Spring Bean 컨테이너가 사용할 설정파일을 지정할 때 사용한다.
 * 
 * class 위의 두 어노테이션으로 인해 테스트 클래스 자체가 Bean 객체가 되어 Spring에서 관리하게 된다. 
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {ApplicationConfig.class})
public class MyServiceTest {
	 /* MyServiceTest 클래스가 Bean으로 관리되면서,
	 * Spring Bean 컨테이너는 Bean들을 찾아 메모리에 올리고
         * MyService 필드에 객체를 주입할 수 있게 된다.(@Autowired)
	 */
	@Autowired
	MyService myService;
	
	@Test
	public void execute() throws Exception{
		// given
		int value1 = 5;
		int value2 = 10;
		
		// when
		int result = myService.execute(value1, value2);
		
		// then
		Assert.assertEquals(30, result);
	}
}

만약 관계맺은 CalculatorSerivce의 plus() 메서드가 버그, 오류가 있다면 어떻게 될까?
-> 내가 생각한 값이 나오지 않을 것이다.

이런 문제를 해결하기 위한 방법으로 Mock 객체를 이용하는 방법이 있다.

MySerivce가 사용하던 CalculatorService를 사용하는 대신, 가짜 객체를 하나 생성한다. 내가 원하는 동작을 Mock 객체로 CalculatorService를 사용함으로써 MyService의 내용만 테스트를 수행할 수 있다.

Mock 객체를 이용하여 관계 끊고 Unit test 하기

  1. pom.xml 수정 - mockito-core 라이브러리 추가
...
    <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-core</artifactId>
      <version>1.9.5</version>
      <scope>test</scope>
    </dependency>
...
  1. MyServiceTest 수정
package org.edwith.webbe.calculatorcli;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.verify;

@RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
	
	// Mock 객체를 사용하는 MyService 객체를 생성하여 초기화하라는 의미
	@InjectMocks
	MyService myService;
	
	// calculatorService가 Mock 객체를 참조하도록 한다.
	// 자동으로 객체가 생성되고 해당 필드가 초기화 된다는 것을 의미
	@Mock
	CalculatorService calculatorService; // 가짜 객체
	
	@Test
	public void execute() throws Exception{
		// given
		int value1 = 5;
		int value2 = 10;
		
		// given 메서드 : 가짜 객체가 동작하는 방법을 규정
		given(calculatorService.plus(5, 10)).willReturn(15);
		
		// when
		int result = myService.execute(value1, value2);
		
		// then
		// verify 메소드 : 파라미터로 들어온 객체의 plus메소드가 호출된 적이 있는지 검증
		// 이 상황에서는 어떤 정수든지 2개를 파라미터로 넣어서 plus()메소드를 호출했는지를 검증
		verify(calculatorService).plus(anyInt(), anyInt());
		Assert.assertEquals(30, result);
	}
}

결과

profile
Enjoy to study

0개의 댓글