Статьи

Аннотированные контроллеры — Spring Web / Webflux и Тестирование

Spring Webflux и Spring Web — это два совершенно разных веб-стека. Однако Spring Webflux продолжает поддерживать модель программирования на основе аннотаций.

Конечная точка, определенная с использованием этих двух стеков, может выглядеть одинаково, но способ проверки такой конечной точки довольно различен, и пользователь, пишущий такую ​​конечную точку, должен знать, какой стек является активным, и соответствующим образом сформулировать тест.

Пример конечной точки

Рассмотрим пример конечной точки на основе аннотации:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.springframework.web.bind.annotation.PostMapping</span> import org.springframework.web.bind.annotation.PostMapping</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.springframework.web.bind.annotation.RequestBody</span> import org.springframework.web.bind.annotation.RequestBody</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.springframework.web.bind.annotation.RequestMapping</span> import org.springframework.web.bind.annotation.RequestMapping</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.springframework.web.bind.annotation.RestController</span> import org.springframework.web.bind.annotation.RestController</span>
 
 
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">data class Greeting(val message: String)</span> класс данных Greeting (val message: String)</span>
 
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">@RestController</span> @RestController</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">@RequestMapping("/web")</span> @RequestMapping ( "/ веб")</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">class GreetingController {</span> class GreetingController {</span>
     
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">@PostMapping("/greet")</span> @PostMapping ( "/") Greet</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">fun handleGreeting(@RequestBody greeting: Greeting): Greeting {</span> fun handleGreeting (@RequestBodyreeting: Greeting): Приветствие {</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">return Greeting("Thanks: ${greeting.message}")</span> return Greeting ("Спасибо: $ {reeting.message}")</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
     
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

Тестирование с помощью Spring Web

Если для создания этого приложения использовался стартер Spring Boot 2 с Spring Web в качестве стартера, это указывается с помощью файла сборки Gradle следующим образом:

1
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">compile('org.springframework.boot:spring-boot-starter-web')</span> компиляции ( 'org.springframework.boot: весна-загрузка-стартер-сеть')</span>

тогда проверка такой конечной точки будет использовать Mock Web Runtime, называемую Mock MVC :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.junit.Test</span> импорт org.junit.Test</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.junit.runner.RunWith</span> import org.junit.runner.RunWith</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.springframework.beans.factory.annotation.Autowired</span> import org.springframework.beans.factory.annotation.Autowired</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest</span> import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.springframework.test.context.junit4.SpringRunner</span> import org.springframework.test.context.junit4.SpringRunner</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.springframework.test.web.servlet.MockMvc</span> import org.springframework.test.web.servlet.MockMvc</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post</span> import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content</span> import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content</span>
 
 
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">@RunWith(SpringRunner::class)</span> @RunWith (SpringRunner :: класс)</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">@WebMvcTest(GreetingController::class)</span> @WebMvcTest (GreetingController :: класс)</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">class GreetingControllerMockMvcTest {</span> class GreetingControllerMockMvcTest {</span>
 
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">@Autowired</span> @Autowired</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">lateinit var mockMvc: MockMvc</span> lateinit var mockMvc: MockMvc</span>
 
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">@Test</span> @Тест</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">fun testHandleGreetings() {</span> fun testHandleGreetings () {</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">mockMvc</span> mockMvc</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">.perform(</span> .Произвести (</span>
                        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">post("/web/greet")</span> пост ( "/ веб / Greet")</span>
                                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">.content("""</span> .content ( ""»</span>
                                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">|{</span> | {</span>
                                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">|"message": "Hello Web"</span> | "message": "Hello Web"</span>
                                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">|}</span> |}</span>
                            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">""".trimMargin())</span> ""».TrimMargin ())</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">).andExpect(content().json("""</span> ) .AndExpect (содержание (). JSON ( ""»</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">|{</span> | {</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">|"message": "Thanks: Hello Web"</span> | "message": "Спасибо: Hello Web"</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">|}</span> |}</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">""".trimMargin()))</span> ""».TrimMargin ()))</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

Тестирование с помощью Spring Web-Flux

Если, с другой стороны, стартеры Spring-Webflux были задействованы, скажем, со следующей зависимостью Gradle:

1
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">compile('org.springframework.boot:spring-boot-starter-webflux')</span> компиляции ( 'org.springframework.boot: весна-загрузки-стартер-webflux')</span>

тогда тест этой конечной точки будет использовать отличный класс WebTestClient , а именно:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.junit.Test</span> импорт org.junit.Test</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.junit.runner.RunWith</span> import org.junit.runner.RunWith</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.springframework.beans.factory.annotation.Autowired</span> import org.springframework.beans.factory.annotation.Autowired</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest</span> import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.springframework.http.HttpHeaders</span> import org.springframework.http.HttpHeaders</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.springframework.test.context.junit4.SpringRunner</span> import org.springframework.test.context.junit4.SpringRunner</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.springframework.test.web.reactive.server.WebTestClient</span> import org.springframework.test.web.reactive.server.WebTestClient</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">import org.springframework.web.reactive.function.BodyInserters</span> import org.springframework.web.reactive.function.BodyInserters</span>
 
 
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">@RunWith(SpringRunner::class)</span> @RunWith (SpringRunner :: класс)</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">@WebFluxTest(GreetingController::class)</span> @WebFluxTest (GreetingController :: класс)</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">class GreetingControllerTest {</span> class GreetingControllerTest {</span>
 
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">@Autowired</span> @Autowired</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">lateinit var webTestClient: WebTestClient</span> lateinit var webTestClient: WebTestClient</span>
 
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">@Test</span> @Тест</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">fun testHandleGreetings() {</span> fun testHandleGreetings () {</span>
        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">webTestClient.post()</span> webTestClient.post ()</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">.uri("/web/greet")</span> .uri ( "/ веб / Greet")</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">.header(HttpHeaders.CONTENT_TYPE, "application/json")</span> .header (HttpHeaders.CONTENT_TYPE, "application / json")</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">.body(BodyInserters</span> .Body (BodyInserters</span>
                        <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">.fromObject("""</span> .fromObject ( ""»</span>
                                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">|{</span> | {</span>
                                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">|</span> |</span> <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"message": "Hello Web"</span> "message": "Hello Web"</span>
                                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">|}</span> |}</span>
                            <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">""".trimMargin()))</span> ""».TrimMargin ()))</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">.exchange()</span> .exchange ()</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">.expectStatus().isOk</span> .expectStatus (). isOk</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">.expectBody()</span> .expectBody ()</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">.json("""</span> .json ( ""»</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">|{</span> | {</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">|</span> |</span> <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">"message": "Thanks: Hello Web"</span> "message": "Спасибо: Hello Web"</span>
                    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">|}</span> |}</span>
                <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">""".trimMargin())</span> ""».TrimMargin ())</span>
    <span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>
<span class="notranslate" onmouseover="_tipon(this)" onmouseout="_tipoff()"><span class="google-src-text" style="direction: ltr; text-align: left">}</span> }</span>

Вывод

Легко предположить, что, поскольку модель программирования выглядит очень похожей при использовании стеков Spring Web и Spring Webflux, тесты для такого унаследованного теста с использованием Spring Web продолжались бы до Spring Webflux, однако это не так, поскольку у нас есть разработчик помнить о базовом стеке, который вступает в игру, и формулировать тест соответствующим образом. Я надеюсь, что этот пост проясняет, как такой тест должен быть обработан.

Смотрите оригинальную статью здесь: Аннотированные контроллеры — Spring Web / Webflux и Тестирование

Мнения, высказанные участниками Java Code Geeks, являются их собственными.