ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring 5.2 와 Spring boot 2.2 추가된 Test 기능들
    카테고리 없음 2023. 4. 23. 14:06
    오늘 알아볼 내용은 Spring 과 Spring boot의 새로운 Test 기능들을 알아볼 예정이다. 아주 많은 기능을 이야기 할건 아니지만 주로 많이 사용될 만한 것들을 살펴볼 예정이다. 일단 Spring 5.2에 추가된 2가지 내용을 살펴보도록 하자.

    생성자 @Autowired

    Spring5 부터는 junit5를 적극적으로 지원하기 시작했다. junit5를 사용하기전에는 다음과 같은 코드를 작성해야 했었다.
    @RunWith(SpringRunner.class)
    @DataJpaTest
    public class UserRepositoryTests {
    
        @Autowired
        private AccountRepository accountRepository;
    
        @Test
        public void findAllTest() {
    
            assertThat(accountRepository.findAll())
                    .isEqualTo(Collections.singletonList(new Account(1L, "wonwoo", 22)));
        }
    
    }
    
    위의 코드는 junit4로 작성된 코드이다. junit4에선 디폴트 생성자가 있어야 했다. 그래서 위와 같이 @Autowired을 필드에 주입했어야 했지만 junit5부턴 그럴필요 없다. junit5에선 생성자를 통해 주입받을 수있게 되었다.
    @DataJpaTest
    class UserRepositoryTests {
    
        private final AccountRepository accountRepository;
    
        @Autowired
        private UserRepositoryTests(AccountRepository accountRepository) {
            this.accountRepository = accountRepository;
        }
    
    
        @Test
        void findAllTest() {
    
            // (대충 테스트한다는 내용)
        }
    
    }
    
    
    여기까지는 Spring 5.0에서도 가능했던 기능이지만 이제는 생성자에도 @Autowired 애노테이션을 제거할 수도 있게 되었다. 하지만 바로 되는건 아니고 두가지 설정 방법이 존재한다. 첫 번째로는 애노테이션을 사용하는 방법이 존재한다.
    @DataJpaTest
    @TestConstructor(autowireMode = AutowireMode.ALL)
    class UserRepositoryTests {
    
        private final AccountRepository accountRepository;
    
        private UserRepositoryTests(AccountRepository accountRepository) {
            this.accountRepository = accountRepository;
        }
    
        @Test
        void findAllTest() {
    
           // (대충 테스트한다는 내용)
    
        }
    
    }
    
    
    AutowireMode.ALL 타입말고 AutowireMode.ANNOTATED 타입이 더 존재하는데 이건 그냥 안쓰는 것과 동일한 기능으로 보인다. 실제 어노테이션을 사용한다는 의미의 타입으로 보여진다. 다음은 두번째 방법인데 이건 전역으로 설정할 수 있다. 아마도 이 기능 때문에 AutowireMode.ANNOTATED 타입이 존재하는듯 싶다. 전역으로 설정한 값보다 개별로한 설정이 나중에 동작해 전역 설정을 덮어 쓰게 되어있다. 전역 설정은 아주 간단하다. spring.properties 파일을 클래스패스에 만들어서 spring.test.constructor.autowire.mode=ALL 이와 같이 작성해주면 전역설정이 된다.
    spring.test.constructor.autowire.mode=ALL
    
    설정 후에 아래와 같이 @Autowired 와 @TestConstructor 애노테이션이 없어도 테스트가 가능해진다.
    @DataJpaTest
    class UserRepositoryTests {
    
        private final AccountRepository accountRepository;
    
        private UserRepositoryTests(AccountRepository accountRepository) {
            this.accountRepository = accountRepository;
        }
    
        @Test
        void findAllTest() {
    
           // (대충 테스트한다는 내용)
    
        }
    
    }
    
    
    그럼 위와 같이 작성해도 테스트를 진행할 수 있다. 위에서 말했다시피 전역설정후 개별 테스트에서는 전역 설정을 사용하고 싶지 않다면 아래와 같이 다시 작성해주면 된다.
    @DataJpaTest
    @TestConstructor(autowireMode = ANNOTATED)
    class UserRepositoryTests {
    
        private final AccountRepository accountRepository;
    
        private UserRepositoryTests(AccountRepository accountRepository) {
            this.accountRepository = accountRepository;
        }
    
        @Test
        void findAllTest() {
    
            // (대충 테스트한다는 내용)
        }
    
    }
    
    
    그럼 위의 코드는 테스트를 진행 할 수 없다. 만약 테스트를 진행하고 싶다면 @Autowired을 작성해줘야 한다.

    kotlin dsl web test

    spring 5.2 부터는 webmvc에도 dsl을 지원한다. 기존에는 (5.0에는) webflux만 지원했지만 이제는 우리가 흔히 사용하는 webmvc도 지원하게 되었다. 이렇게 webmvc를 지원하면서 webmvctest도 dsl을 지원하게 되었다. 사용법은 아주 간단하다. [kotlin]\r @Test\r fun `find all users test`() {\r \r given(accountRepository.findAll()).willReturn(listOf(\r Account(1, "wonwoo", 22),\r Account(2, "foo", 32),\r Account(3, "bar", 28)))\r \r mockMvc.get("/api/users") {\r accept = APPLICATION_JSON\r headers {\r contentLanguage = Locale.KOREA\r }\r }.andExpect {\r status { isOk }\r content { contentType(APPLICATION_JSON) }\r jsonPath("$[0].name") { value("wonwoo") }\r jsonPath("$[1].name") { value("foo") }\r jsonPath("$[2].name") { value("bar") }\r content { json("""[{"name":"wonwoo","age":22},{"name":"foo","age":32},{"name":"bar","age":28}]""") }\r }.andDo {\r print()\r }\r }\r [/kotlin] 위와 같이 MockMvc 에 확장함수들이 추가 되었고 MockHttpServletRequestDsl, ResultActionsDsl, MockMvcResultMatchersDsl, MockMvcResultHandlersDsl 등 몇가지 dsl 클래스들이 새로 추가가 되었다. POST 테스트 또한 비슷한 맥략이다. [kotlin]\r @Test\r fun `user save test`() {\r given(accountRepository.save(Account(name = "test", age = 11))).willReturn(\r Account(1, "test", 11))\r \r mockMvc.post("/api/users") {\r accept = APPLICATION_JSON\r contentType = APPLICATION_JSON\r content = """{"name":"test", "age":11}"""\r headers {\r contentLanguage = Locale.KOREA\r }\r }.andExpect {\r status { isOk }\r content { contentType(APPLICATION_JSON) }\r jsonPath("$.name") { value("test") }\r jsonPath("$.age") { value("11") }\r content { json("""{"name":"test","age":11}""") }\r }.andDo {\r print()\r }\r }\r [/kotlin] 이제는 Spring boot와 관련된 기능들이다. 소소한 것들이니 참고만 하면 되겠다.

    junit5

    spring boot 2.2부터는 기본적으로 junit5를 디펜더시 받고 있다. 뿐만 아니라 junit vintage engine 도 추가적으로 디펜더시를 받고 있어 만약 junit5가 익숙하지 않는 분들은 그냥 junit4를 이용해도 무방하다.
    import org.junit.Test;
    
    public class Junit4Tests {
    
        @Test
        public void test() {
    
        }
    }
    
    
    import org.junit.jupiter.api.Test;
    
    class Junit5Tests {
    
        @Test
        void test() {
    
        }
    }
    
    
    원하는 버전 아무거나 사용해도 무방하다.

    ApplicationArguments

    사용할일이 있을 수 있겠지만 아직은 무엇에 어떻게 사용할지 생각이 나지는 않는다. 이런기능도 있으니 만약 사용할일이 있다면 사용해도 좋다. @SpringBootTest를 사용해 테스트를 한다면 @SpringBootTest의 args 속성을 넣어 아규먼트를 꺼내 사용할 수 있다.
    @SpringBootTest(args = "--foo=bar")
    class TestArgs {
    
        private final ApplicationArguments applicationArguments;
    
        @Autowired
        private TestArgs(ApplicationArguments applicationArguments) {
            this.applicationArguments = applicationArguments;
        }
    
        @Test
        void test() {
            List<String> optionValues = applicationArguments.getOptionValues("foo");
    
            //대충 argument를 사용한다는 내용
        }
    
    }
    
    
    위와 같이 ApplicationArguments 인터페이스를 주입받은 후에 원하는 테스트에 arguments를 꺼내 사용할 수 있다. ApplicationArguments API는 문서를 통해 확인 할 수 있다.

    OutputCaptureExtension

    마지막으로 OutputCapture와 관련된 내용이다. 예전 junit4에서 사용했던 OutputCaptureRule과 동일한 기능이지만 이것은 junit5 위한 Extension 기능이다. 간단하게 OutputCapture 의 기능을 설명하자면 log나 print를 찍은, 즉 console에 찍히는 내용을 캡쳐해서 그 내용을 검증하는 기능이다. 예전에 junit4의 경우엔 다음과 같이 작성하였다.
    public class OutputCaptureRuleTests {
    
        @Rule
        public final OutputCaptureRule output = new OutputCaptureRule();
    
        @Test
        public void test() {
            System.out.println("hello world");
            assertThat(output).contains("hello world");
        }
    
    }
    
    
    콘솔에 hello world라는 내용을 찍고 그것을 검증하는 내용이다. @Rule 이라는 애노테이션은 junit4에만 존재하기에 junit5에선 사용할 수 없다. 그래서 spring boot 2.2부터 OutputCaptureExtension 클래스가 추가 되었다. 사용법은 아주 간단하다. 위의 junit4와 비슷하다.
    @ExtendWith(OutputCaptureExtension.class)
    class OutputCaptureExtensionTests {
    
        @Test
        void outputTest(CapturedOutput capturedOutput) {
    
            System.out.println("hello world");
            assertThat(capturedOutput).contains("hello world");
    
        }
    }
    
    
    위와 같이 작성해도 되며 다른 곳에도 사용한다면 생성자에 주입받아 사용해도 된다.
    @ExtendWith(OutputCaptureExtension.class)
    class OutputCaptureExtensionTests {
    
        private final CapturedOutput capturedOutput;
    
        OutputCaptureExtensionTests(CapturedOutput capturedOutput) {
            this.capturedOutput = capturedOutput;
        }
    
        // ...
    }
    
    오늘은 이렇게 test와 관련된 내용을 작성해봤다. 몇가지는 필자도 자주 사용할 것 같지만 ApplicationArgumentsOutputCaptureExtension 은 그리 많이 사용할 것 같진 않다. 예전에도 OutputCaptureRule 은 거의 사용하진 않았으니 말이다. 만약 webmvc dsl test나 webmvc dsl 에 관심이 있다면 은 여기에 소스가 있으니 참고하면 되겠다.

    댓글

Designed by Tistory.