[Spring] Test 코드 (JUnit 4)

주재완·2024년 6월 13일
0

Spring

목록 보기
3/4
post-thumbnail

TDD

테스트 주도 개발(Test-driven development TDD)은 매우 짧은 개발 사이클을 반복하는 소프트웨어 개발 프로세스 중 하나입니다. TDD는 다음과 같은 3단계로 이루어져 있고, 이를 계속 반복하는 작업입니다.

  1. 요구사항을 검증을 위한 자동화 테스트 케이스 작성
  2. 테스트 케이스를 통과하기 위한 최소한의 코드를 생성
  3. 표준에 맞도록 리팩토링

TDD의 장점으로는 메소드 단위로 작업하기에 메소드에는 정말 필요한 기능만 정의하게 됩니다. 그리고 불확실한 변수, 메소드, 로직들이 들어가지 않아 클린함수를 구현할 수 있게 됩니다.

Set-up

우선 아래와 같이 junit과 spring-test를 pom.xml에 dependency를 추가해줍니다.
자바 테스트를 사용하기 위해서는 기본적으로는 juint이 필요합니다. 그리고 Spring MVC 웹 애플리케이션 테이스를 위해서 MockMvc가 필요한데, 이를 제공해주는 spring-test 역시 추가해줍니다.

junit의 경우 4.12 이상을 사용하는 것을 추천하고(여기서는 4.13 사용),
spring-test는 사용하고 있는 스프링 버전에 맞추어 줍니다.
또한 servlet의 경우 @WebAppConfiguration을 사용하기 위해 3.0 이상으로 만들어 줍니다.

<!-- junit -->
<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.13</version>
		<scope>test</scope>
</dependency>

<!-- spring-test -->	
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>${org.springframework-version}</version>
</dependency>

<!-- 서블릿 버전을 3.0이상으로 설정 -->	
<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
			<scope>provided</scope>
</dependency>

Test Annotation

@Test : 해당 메소드가 테스트 대상 메소드임을 의미, junit 4의 경우 메소드가 public이여야함 (junit 5부터는 public일 필요 없음)

@Test(timeout=10) : 테스트 메소드 수행 제한 시간이 10 ms

@Test(expected=RuntimeException.class) : RuntimeException이 발생하면 테스트 성공, 아니면 실패

@Before : 각 테스트 메소드에서 공통적으로 실행해야 할 작업 포함, 테스트 메소드가 실행되기 전에 실행됨 (junit 5부터는 @BeforeEach)

@After : 각 테스트 메소드의 실행이 완료된 후에 동일한 마무리 작업을 수행할 경우 사용, 테스트 메소드가 실행된 후에 실행됨 (junit 5부터는 @AfterEach)

@BeforeClass : static 메소드이며, 모든 테스트 메소드가 동작하기 전 공통적으로 실행해야 하는 작업 수행 (junit 5부터는 @BeforeAll)

@AfterClass : static 메소드이며, 모든 테스트 메소드가 생행되고 난 후에 한번 실행하는 메소드 (junit 5부터는 @AfterAll)

Assert 구문(JUnit 4)

Assert는 "확인"을 수행하는 동작입니다.

  • assertEquals([String message,] expected, actual): 두 값이 동일한지 검사한다.
assertEquals("메시지", 5, 2 + 3);
  • assertNotEquals([String message,] unexpected, actual): 두 값이 동일하지 않은지 검사합니다.
assertNotEquals("메시지", 5, 2 + 2);
  • assertTrue([String message,] boolean condition): 조건이 참인지 검사합니다.
assertTrue("메시지", 3 < 5);
  • assertFalse([String message,] boolean condition): 조건이 거짓인지 검사합니다.
assertFalse("메시지", 3 > 5);
  • assertNull([String message,] object): 객체가 null인지 검사합니다.
assertNull("메시지", null);
  • assertNotNull([String message,] object): 객체가 null이 아닌지 검사합니다.
assertNotNull("메시지", new Object());
  • assertSame([String message,] expected, actual): 두 객체가 같은 객체인지 (== 연산자로 비교) 검사합니다.
Object obj = new Object();
assertSame("메시지", obj, obj);
  • assertNotSame([String message,] unexpected, actual): 두 객체가 같은 객체가 아닌지 검사합니다.
Object obj1 = new Object();
Object obj2 = new Object();
assertNotSame("메시지", obj1, obj2);
  • assertArrayEquals([String message,] expectedArray, actualArray): 배열이 동일한지 검사합니다.
int[] expected = {1, 2, 3};
int[] actual = {1, 2, 3};
assertArrayEquals("메시지", expected, actual);
  • fail([String message]): 테스트를 실패로 처리합니다. 주로 특정 조건에 도달하면 테스트를 실패시키기 위해 사용됩니다. STS 환경에서 처음 test case 만들시 default로 작성됩니다.

WebApplicationContext (wac)

WebApplicationContext는 spring의 ApplicationContext를 확장한 것으로, 웹 애플리케이션의 컨텍스트를 제공하는 역할을 합니다. 이는 웹 애플리케이션의 구성, 웹 환경에서의 빈(bean) 관리, 그리고 웹 관련 설정을 포함합니다.

MockMvc (mockMvc)

MockMvc는 스프링 MVC 애플리케이션의 웹 계층을 테스트하기 위한 유틸리티로, 실제 서버를 구동하지 않고도 MVC 컨트롤러를 테스트할 수 있게 해줍니다. 이는 DispatcherServlet을 직접 호출하여 요청과 응답을 모킹(mocking)함으로써 작동합니다.

예시 1

아주 간단한 로그인을 확인해보겠습니다.

@RequiredArgsConstructor
@Controller
public class MemberController {

	private final MemberService memberService;

	// 아이디체크
	@ResponseBody
	@PostMapping("login.me")
	public String idCheck(Member member) {
		int result = memberService.idCheck(member);

		if (result > 0) {
			return "redirect:/";
		} else {
			return "redirect:/fail";
		}
	}
}

그리고 테스트 코드의 경우 다음과 같습니다.

@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {
		"file:src/test/resources/root-context.xml",
		"file:src/test/resources/servlet-context.xml",
		"file:src/test/resources/spring-security.xml"
})
public class MemberControllerTest {
	
	@Autowired
	private WebApplicationContext wac;// 웹어플리케이션 컨텍스트
	
	private MockMvc mockMvc; // HTTP요청 및 응답을 모의로 테스트 할 수 있는 객체
	
	@Before // Test메서드가 실행되기전에 실행하는 메서드
	public void setup() {
		this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); // mockMvc에 컨텍스트 정보를 저장
		log.info("===============mockMvc 준비 완료========================");
	}
	

	@Test
	public void testLoginMember() {
		try {
			mockMvc.perform(post("/login.me") // POST 메서드로 /login.me 요청
					.param("userId", "admin") // 요청 파라미터 userId 세팅
					.param("userPwd", "1234"))
			.andDo(print()) // 요청한거에대한 응답코드를 console에 출력
			.andExpect(status().isFound())// HTTP 상태코드가 302인지 확인 - redirect
			.andExpect(redirectedUrl("/"));  // 리다이렉트 경로 확인
//			.andExpect(status().isOk()); // HTTP 상태코드가 200 인지 확인
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

예시 2(MvcResult 활용)

응답값이 있어 이를 비교하기 위해서는 MvcResult로 받아서 이를 비교해야합니다.

간단한 아이디체크를 확인해보겠습니다. 실제로그인은 아닙니다

@RequiredArgsConstructor
@Controller
public class MemberController {

	private final MemberService memberService;

	// 로그인
	@ResponseBody
	@PostMapping("idCheck.me")
	public String idCheck(String checkId) {
		int result = memberService.idCheck(checkId);

		if (result > 0) {
			return "NNNNY";
		} else {
			return "NNNNN";
		}
	}
}

그리고 Test 코드의 경우 다음과 같이 작성합니다.

@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {
		"file:src/test/resources/root-context.xml",
		"file:src/test/resources/servlet-context.xml",
		"file:src/test/resources/spring-security.xml"
})
public class MemberControllerTest {
	
	@Autowired
	private WebApplicationContext wac; // 웹어플리케이션 컨텍스트

	private MockMvc mockMvc; // HTTP요청 및 응답을 모의로 테스트 할 수 있는 객체
	
	@Before // Test메서드가 실행되기 전에 실행하는 메서드
	public void setup() {
		this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); // mockMvc에 컨텍스트 정보를 저장
		log.info("====================mockMvc 준비 완료====================");
	}

	@Test
	public void testIdCheck() {
		MvcResult result;
		try {
			result = mockMvc.perform(
					post("/idCheck.me") // POST 메서드로 /login.me 요청
					.param("checkId", "admin")) // 요청 파라미터 userId 세팅
					.andExpect(status().isOk()) // 200
					.andReturn(); // 
			
	        // 응답 본문의 내용 확인 
	        String content = result.getResponse().getContentAsString();
	        assertEquals("NNNNY", content); // 아이디 사용가능 결과와 비교
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}
profile
언제나 탐구하고 공부하는 개발자, 주재완입니다.

0개의 댓글

관련 채용 정보