블로그 자료를 이전하고 있습니다. 많은방문 부탁드립니다 ( https://hyeonguj.github.io/ )

이 블로그의 자료는 이관된 페이지에서도 보실 수 있습니다.

https://hyeonguj.github.io/2019/01/31/spring-unit-test/


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.do”를 호출 했을때 해당 controller를 호출하는지, 그리고 2xx, 3xx와같은 결과를 받는지
mockMvc.perform(get("/partyDetail.do")
				.param("partyId","1")
				.session(httpSession)
				).andExpect(status().isOk());

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

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

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

verify(partyService).selectParty(1);

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

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

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

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

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

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



Controller 는 Unit Test 가 아닌 통합 테스트로 진행 했습니다.

유닛테스트로 구현 할 경우, 인터페이스의 구현체를 생성자를 통해 생성한 후 호출하여 테스트합니다.

이렇게 할 경우 Spring 에 대한 의존성을 모두 삭제합니다.



Service

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

@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 이 필요한데, Spring 에서 제공하는 rollback은 mybatis와 함깨 사용 할 수 없다. (따라서 수동으로…)

@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된것이 아니기 때문이다. 함깨 테스트를 해도 되는것인지, 그래도 각각을 분리해서 테스트를 해야하는것인지는 잘 모르겠다.



DAO 는 Unit Test 가 아닌 통합 테스트로 진행 했습니다.

DAO특성상 DB와 함깨 연동 하는것을 테스트 하였습니다.


느낀점

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

목표로한 coverage가 올라가는것을 보니 기분이 좋았다.

coverage.PNG

+ Recent posts