Spring - Controller Test

Panda·2022년 1월 26일
0

Spring

목록 보기
1/44

SpringBoot를 기반으로 프로젝트를 진행하였습니다.
Spring에서 Controller에 대해 Test 프로그래밍을 공부 하였다.

먼저 ControllerTest 파일을 만들게되면

@SpringBootTest
@AutoConfigureMockMvc
internal class BankControllerTest @Autowired constructor(
    val mockMvc: MockMvc,
    val objectMapper: ObjectMapper
)

이러한 양식으로 작성을 했습니다.
Test환경을 갖추기 위해 @SpringBootTest
mockMvc를 쓰기위해 @AutoConfigureMockMvc를 사용했고
@Autowired가 mockMvc와 objectMapper를 DI 하게 됩니다.
@Autowired : 필요한 의존 객체의 “타입"에 해당하는 IoC 컨테이너 안에 존재하는 Bean을 찾아 주입합니다.

    @Nested
    @DisplayName("DELETE /api/banks/{accountNumber}")
    @TestInstance(TestInstance.Lifecycle.PER_CLASS)
    inner class DeleteExistingBank {
        @Test
        @DirtiesContext
        fun `should delete the bank with the given account number`() {
            // given
            val accountNumber = 123456
            // when/then
            mockMvc.delete("$baseUrl/$accountNumber")
                .andDo { print() }
                .andExpect { status { isNoContent() } }

            mockMvc.get("$baseUrl/$accountNumber")
                .andExpect { status { isNotFound()  } }
        }

        @Test
        fun `should return NOT FOUND if no bank with given account number exists`() {
            // given
            val invalidAccountNumber = "does_not_exist"
            // when/then
            // mockMVC DSL
            mockMvc.delete("$baseUrl/$invalidAccountNumber")
                .andDo { print() }
                .andExpect { status { isNotFound() } }
          
        }
    }

Spring Test에서 같은 context를 사용하는 테스트(같은 context.xml 파일을 이용해서 생성되거나,
같은 SpringBootApplication 이용)가 여러 개 있을 때 각각의 테스트마다 새로운 context를 생성하는게 아니라
기존의 context를 재활용하기 때문에 발생하는 문제습니다.
-> 따라서 그냥 단순히 inner class test로 테스트를 하게 되면 독립적인 테스트 환경을 구성을 못함!!

TDD 규칙중에 F.I.R.S.T 중 I (Isolated) 를 지키기 위해서
임시로 @DirtiesContext를 Delete 수행 테스트 메소드에 적용하였습니다.

  • @DirtiesContext : 어노테이션을 통해 테스트를 수행하기 전, 수행한 이후,
    그리고 테스트의 각 테스트 케이스마다 수행하기 전, 수행한 이후에 context를 다시 생성하도록 지시할 수 있고
    메소드 뿐만 아니라 클래스 레밸도 적용가능합니다.
  • @Nested : 중첩 구조로 테스트 할 수 있게 만듬. (가독성 높이기 위해서 사용할 듯?)
  • @DisplayName : 중첩 구조의 이름 표시
  • @TestInstance : Test 인스턴스의 Lifecycle 결정.
  • @Test : 해당 메소드를 Test 수행 대상이 됨.

가독성을 높이기 위해 mockMVC DSL을 사용하여 프로그래밍을 하였습니다.

@Test
fun `should add the new bank`() {
	//given
    	val newBank = Bank("acc123", 31.415, 2)

	//when
    	val performPost = mockMvc.post(baseUrl) {
        	contentType = MediaType.APPLICATION_JSON
            	content = objectMapper.writeValueAsString(newBank)
       	}

	// then
    	performPost
        	.andDo { print() }
            	.andExpect {
                    status { isCreated() }
                    content {
                        contentType(MediaType.APPLICATION_JSON)
                        json(objectMapper.writeValueAsString(newBank))
                    }
//                    jsonPath("$.accountNumber") { value("acc123") }
//                    jsonPath("$.trust") { value("31.415") }
//                    jsonPath("$.transactionFee") { value("2") }
                }

mockMvc.get("$baseUrl/${newBank.accountNumber}")
	.andExpect { 
    		content { json(objectMapper.writeValueAsString(newBank)) } }
    	}

이런식으로
header 설정(contentType) 이라던지 body(content)를 설정하고
http통신을 수행할 수 있었습니다.

위와 같은 방식으로
getBanks, getBank, addBank, updateBank, deleteBank 에 대해서
Test를 진행하였습니다.

Refrences

https://shortstories.gitbooks.io/studybook/content/dirtiescontext.html

https://www.youtube.com/watchv=TJcshrJOnsE&list=PL6gx4Cwl9DGDPsneZWaOFg0H2wsundyGr&index=2

profile
실력있는 개발자가 되보자!

0개의 댓글