spring mvc 에서 unit test 구현 (Controller, Service, DAO)

Unit test 구현

Spring MVC 구조에서 역할에 따른 Unit test

‘lets party’ 라는 이름으로 프로젝트를 진행중이다.
이때 Spring MVC 로 개발을 하였고 이에 따른 테스트를 짜보려 한다.

‘lets party’는 크게 3개의 레이어로 구성되어 있다.

  • Controller :
    1. 처리해야 할 데이터를 브라우저에게 받는다.
    2. 담당할 service를 선택하여 호출한다
    3. 처리한 데이터를 다음 페이지에서 볼 수 있게 셋팅한다.
    4. 이동할 페이지를 리턴한다
  • Service :
    1. 데이터를 받아 비지니스 로직을 처리한다.
    2. DB의 활용이 필요한 경우 해당 처리를 하는 DAO를 호출한다
  • DAO
    1. db를 활용할 데이터를 받는다
    2. 역할을 하는 mapper를 호출하여 처리하게 한다.

이와 같이 역할이 다르다 보니 테스트 하는 방법도 달라진다.
기본원칙은 ‘분리’
서로가 서로에게 의존하지 않아서, 다른 객체와는 상관 없이 항상 같은 결과가 나와야 한다.
(하면서 배웠지만, 적용이 안된 부분도 많다..)

각각의 테스트 방법이 조금씩 다르지만 기본적으로 하는일은 똑같다.
목적 : input에 대한 output이 잘 나오는지 확인하기!
과정 : 입력값 가정하기 -> 호출하기 -> 검증하기 (호출검증 및 output 검증)

Controller

Controller의 테스트는 크게 2가지를 테스트 하였다

  1. 브라우저에서 “/pageName”를 호출 했을때 해당 controller를 호출하는지, 그리고 2xx, 3xx와같은 결과를 받는지
1
2
3
4
mockMvc.perform(get("/partyDetail")
.param("partyId","1")
.session(httpSession)
).andExpect(status().isOk());

위 코드는 “/partyDetail” 를 브라우저에서 호출했을때, 해당 controller가 잘 호출 되는지를 확인 한 것이다.
get방식으로 전달된 데이터는 param() 으로 받을 수 있고, HttpSession은 .session으로 받는다.

  1. Controller에서 재대로 된 Service를 호출 하는지

다른 함수가 잘 호출 되었는지는 Mockito의 verify를 활용하여 검사 할 수 있다.

1
verify(partyService).selectParty(1);

위 함수는 partyService.selectParty() 함수가 1이라는 인자값을 통해 호출되었는지 확인하는 함수이다.

그 외에도 다양한 방법으로 함수호출을 test 할 수 있다.

1
verify(partyService, times(3)).selectParty(1);

times(3) 을 통해 3번 호출 되었는지 검사한다.

1
verify(partyService, times(3)).selectParty(anyInt());

anyInt()를 통해서는 ‘1’이라는 인자 외에 int형으로 된 값으로 호출되었는지 테스트 할 수 있다.

Service

service는 비지니스 로직을 처리하는 부분으로 test도 상대적으로 좀 복잡하다.
일반적으로 과정은 다음과 같다

1
2
3
4
5
6
7
8
9
10
11
@Test
public void joinPartyFirstJoinTest() {
when(partyDAO.getNumOfUserParty(Mockito.any(UserPartyVO.class))).thenReturn(0);
when(partyDAO.checkJoin(Mockito.any(UserPartyVO.class))).thenReturn(0);

partyService.joinParty(userPartyVO);

verify(partyDAO).getNumOfUserParty(Mockito.any(UserPartyVO.class));
verify(partyDAO).insertUserParty(Mockito.any(UserPartyVO.class));
verify(partyDAO).increasePublicParticipantsCount(anyInt());
}

when().thenReturn() 을 통해 비지니스 로직에서 쓰이는 의존성 있는 함수들의 값들을 미리 세팅한다.
이후 함수를 호출하고 (partyService.joinParty(userPartyVO))
다른 함수들을 잘 호출 했는지, 기능을 잘수행했는지 검증한다.

DAO

DAO 는 데이터를 가지고 오는것을 목표로 하고있다.
이후 교육과정에서 Unit test라기보다는 integration test이라고 부른다.
DB에 의존성이 있고, 이를 잘 수행하여 반영이 되는지를 테스트 하기 때문.

letsparty에서는 DAO는 한번에 하나의 쿼리만 처리하게 되어 있기 때문에 간단하게 테스트 한다.

단, test 전 후에 DB의 상태가 같아야 하기 때문에 rollback 이 필요한데,
rollback은 수동으로 처리하였다

(@Test 와 @Transactional을 활용해서 롤백도 가능 http://credemol.blogspot.com/2011/01/spring-junit-transaction.html)

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void insertPartyTest() throws Exception {
int sizeOfBeforeInsert = partyDAO.selectAll().size();

partyDAO.insert(partyVO);
int insertedPId = partyVO.getPartyId();
int sizeOfAfterInsert = partyDAO.selectAll().size();

assertEquals("insertTest - inserted party id : "+insertedPId , sizeOfBeforeInsert+1, sizeOfAfterInsert);

//clean database
partyDAO.deletPartyeById(insertedPId);
}

insert를 하는 DAO를 테스트하는 코드이다.
partyDAO.selectAll(), partyDAO.deletPartyeById() 도 함깨 사용하는데,
partyDAO.selectAll() 를 사용하여 insert 전 후에 크기를 비교하여 1개가 잘 들어갔는지 확인한다.
이후 partyDAO.deletPartyeById()를 통해 rollback 한다.

이 함수들도 모두 test를 해야한다. selectAll()함수가 잘 동작하지 않는다면 insert는 재대로 test된것이 아니기 때문이다.
함깨 테스트를 해도 되는것인지, 그래도 각각을 분리해서 테스트를 해야하는것인지는 잘 모르겠다.

느낀점

test코드를 작성하다보니 기존 코드의 단점이나 수정해야할 부분도 함깨 보이기 시작했다.
test작성만 하는것이 아니라 리팩토링까지 함깨 하다보니 시간이 꽤 걸렸다

테스트 코드가 하나씩 늘어나니 기분이 좋다

Comments