[Mockito]Spring framework에서 Controller 테스트케이스 만들기

요즘 저에게 가장 화두가 되고 있는 단어 중에 하나가 바로 “테스트“입니다.

어떻게 하면, 테스트케이스를 통해 실제 코드를 더욱 견고하고 깔끔하게 만드는가 하는데에

따른 고민에서 시작됩니다.

바로 그 시작점인 “테스트“케이스를 만드는데 있어, 현재 가장 많이 쓰이고 있는

프레임워크인 “스프링“에서 웹 어플리케이션의 서버사이드 “프론트“를 담당하고 있는

“Contoller”부분에 대한 간단한 CRUD 테스트케이스를 만들어 보았습니다.

public class TempVo {

    private Integer id;
    private String name;

    ... 이하 setter/getter코드 생략

}

 

public interface TempService {

    TempVo getListUserInfo(int id);

    int insertUserInfo(TempVo temp);

    int updateUserInfo(TempVo temp);

    int deleteUserInfo(TempVo temp);

    List getListTemp();
}

 

import static com.glider.framework.test.ReflectionInjectorUtils.injector;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.web.servlet.ModelAndView;

import com.google.common.collect.Lists;

@RunWith(MockitoJUnitRunner.class)
public class CommonControllerTest {

	// 테스트대상 클래스의 의존객체에 Mock Object 주입
	@Mock
	private TempService tempService;

	// 테스트대상 클래스에 Mock Object 주입
	@InjectMocks
	private CommonController commonController = new CommonController();

	private ModelAndView model;

	@Before
	public void setUp() {
		// 테스트대상 Mock 클래스에 의존 Mock객체를 쉽게 주입할 수 있음.
		// http://wiki.kwonnam.pe.kr/java/unittest/reflectioninjector 참고.
		injector(commonController).set(tempService);
		model = new ModelAndView();
	}

	@Test
	public void getTemp() {
		int id = 1;
		TempVo temp = new TempVo();

		when(tempService.getListUserInfo(id)).thenReturn(temp);

		commonController.temp(id, model);

		verify(tempService).getListUserInfo(id);
		assertThat(model.getViewName(), is("temp/temp"));
	}

	@Test
	public void insertTemp(){
		TempVo temp = new TempVo();
		temp.setId(1);
		temp.setName("bluepoet");

		when(tempService.insertUserInfo(temp)).thenReturn(1);

		commonController.insertTemp(name, model);

		verify(tempService).insertUserInfo(temp);
		assertThat(model.getViewName(), is("temp/temp"));
	}

	@Test
	public void updateTemp() throws Throwable {
		TempVo temp = new TempVo();
		temp.setId(3);
		temp.setName("bluepoet");

		when(tempService.updateUserInfo(temp)).thenReturn(1);

		commonController.updateTemp(name, model);

		verify(tempService).updateUserInfo(temp);
		assertThat(model.getViewName(), is("temp/temp"));
	}

	@Test
	public void deleteTemp() throws Throwable {
		TempVo temp = new TempVo();
		temp.setId(1);
		temp.setName("bluepoet");

		when(tempService.deleteUserInfo(temp)).thenReturn(1);

		commonController.deleteTemp(name, model);

		verify(tempService).deleteUserInfo(temp);
		assertThat(model.getViewName(), is("temp/temp"));
	}

	@Test
	public void getListTemp() {
		List list = Lists.newArrayList();

		when(tempService.getListTemp()).thenReturn(list);

		commonController.getListTemp(model);

		verify(tempService).getListTemp();
		assertThat(model.getViewName(), is("temp/tempList"));
	}
}

물론 CRUD Controller 메서드안에 더욱 복잡한 로직이 들어갈 수도 있습니다.

그럴땐, 테스트케이스가 더 복잡해지겠지요.

다만, 큰 것을 만들때는 작은 것부터 Step by Step으로 나가는 것이 좋다고 생각합니다.

스프링 framework에서 controller를 만들때는 위 테스트케이스를 뼈대로 해서,

테스트케이스를 확장해나가시면 더욱 견고한 real code를 만드실 수 있으리라고 봅니다.

단위테스트에 대해서는 토비의 스프링3에서도 여러차례 리팩토링과 함께 잘 나와있으니

참고하시면 될것 같구요(테스트대역, 스텁,  Mock Object의 개념등..)

아래 포스팅에서는 테스트케이스를 작성하는 방법과 활용에 대해서 더욱 잘 나와있으니

꼭 일독을 권해드립니다.

* 참고 url

Advertisements

[Spring]@Async 어노테이션 사용하여 비동기로 메서드 실행하기

저번에 회사에서 애플의 apns를 이용해 각각 애플 디바이스로 푸시를 할 수 있는

기능을 만들었습니다.

알람기능은 굳이 “동기“방식으로 구현할 필요가 없었기 때문에 “비동기“방식으로

구현했고, 그때 사용한것이 @Async 어노테이션이었습니다.

@Async에 대한 레퍼런스 문서는 링크를 참조하시면 됩니다.

사용방법은 무척이나 간단합니다.

1. 먼저 비동기로 실행할 놈을 빈으로 설정합니다.

<task:executor id="testExecutor" pool-size="10"/>

<task:annotation-driven executor="testExecutor"/>

pool-size는 각각 상황에 맞게 적절하게 지정해주시면 됩니다.

2. @Async 어노테이션을 비동기로 실행할 메서드에 붙여줍니다.

이때 주의할 점은 @Async 어노테이션이 붙은 메서드의 리턴타입은 void 혹은

    java.util.concurrent.Future타입이라는 것입니다.

다른 타입으로 지정시 무조건 null이 넘어오므로 주의해야 합니다.

@Async
public Future<Integer> test() {
    System.out.println("===========================");
    System.out.println("test메서드 실행###");
    System.out.println("===========================");

    return new AsyncResult<Integer>(-2);
}

마지막으로 간단한 테스트케이스를 작성해 보았습니다.

@Test
public void async_리턴값확인() throws InterruptedException, ExecutionException {
	assertThat(asyncTest.test().get(), is(-2));
}

해당 테스트를 실행하면, 에러없이 결과가 출력됨을 확인할 수 있습니다.

*참고 url