Spring MVC Denetleyicileri için Birim Testleri Yazma: Formlar

In: Genel


Spring MVC Test öğreticimin önceki bölümü bir liste oluşturan bir Spring MVC denetleyicisi için nasıl birim testleri yazabileceğimizi açıkladı. Bu blog yazısı, kullanılan veritabanına veri ekleyen Spring MVC denetleyicileri için birim testleri yazma hakkında daha fazla bilgi sağlar. Daha spesifik olmak gerekirse, bu blog yazısı, bir form gönderen bir Spring MVC denetleyicisi için nasıl birim testleri yazabileceğimizi açıklar.

Bu blog gönderisini bitirdikten sonra:

  • Spring MVC Test çerçevesini kullanarak bir formu nasıl gönderebileceğimizi öğrenin.
  • Geçersiz bilgiler içeren bir form gönderdiğimizde, test edilen sistemin doğru doğrulama hatalarını gösterdiğinden nasıl emin olabileceğimizi anlayın.
  • Doğrulama başarısız olursa, gönderilen formun alanlarının doğru bilgileri içermesini nasıl sağlayabileceğimizi bilin.
  • HTTP isteğinin doğru yola yönlendirildiğini doğrulayabilir.
  • Test edilen sistemin kullanıcıya doğru flaş mesajı göstermesini nasıl sağlayabileceğimizi bilin.

Hadi başlayalım.

Bu blog yazısı şunları varsayar:

Test Edilen Sisteme Giriş

İşlem yapan bir denetleyici yöntemi için birim testleri yazmalıyız. POST istekler şu yola gönderilir: ‘/todo-items’. Bu yöntem, yeni bir yapılacaklar öğesi oluşturur ve kullanıcıyı yapılacaklar görünümü görünümüne yönlendirir. Doğrulama başarısız olursa, bu denetleyici yöntemi HTTP durum kodu 200’ü döndürür ve form görünümünü işler.

Test edilen denetleyici yöntemi denir create() ve şu adımları izleyerek uygulanır:

  1. Gönderilen formda doğrulama hataları varsa, form görünümünün adını döndürün (‘yapılacak öğe/oluştur’).
  2. Çağırarak oluşturulan yapılacaklar öğesini veritabanına kaydedin. create() yöntemi TodoItemCrudService sınıf.
  3. Yeni bir yapılacaklar öğesinin oluşturulduğunu bildiren bir geri bildirim mesajı oluşturun ve bir sonraki görünüm oluşturulduğunda bu mesajın gösterilmesini sağlayın.
  4. HTTP isteğini, oluşturulan yapılacaklar öğesinin bilgilerini işleyen görünüme yönlendirin.

Test edilen kontrolör yönteminin kaynak kodu aşağıdaki gibidir:


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.validation.Valid;
import java.util.Locale;

@Controller
@RequestMapping("/todo-item")
public class TodoItemCrudController {

    private final MessageSource messageSource;
    private final TodoItemCrudService service;

    @Autowired
    public TodoItemCrudController(MessageSource messageSource, 
                                  TodoItemCrudService service) {
        this.messageSource = messageSource;
        this.service = service;
    }

    @PostMapping
    public String create(@Valid @ModelAttribute("todoItem") CreateTodoItemFormDTO form,
                         BindingResult bindingResult,
                         RedirectAttributes redirectAttributes,
                         Locale currentLocale) {
        if (bindingResult.hasErrors()) {
            return "todo-item/create";
        }

        TodoItemDTO created = service.create(form);

        addFeedbackMessage(
                redirectAttributes,
                "feedback.message.todoItem.created",
                currentLocale,
                created.getTitle()
        );

        redirectAttributes.addAttribute("id", created.getId());
        return "redirect:/todo-item/{id}";
    }

    private void addFeedbackMessage(RedirectAttributes attributes,
                                    String messageCode,
                                    Locale currentLocale,
                                    Object... messageParameters) {
        String feedbackMessage = messageSource.getMessage(messageCode,
                messageParameters,
                currentLocale
        );
        attributes.addFlashAttribute("feedbackMessage", feedbackMessage);
    }
}

bu CreateTodoItemFormDTO sınıfı, yeni yapılacaklar öğeleri oluşturmak için kullanılan form nesnesinin bilgilerini içerir. Ayrıca form nesnesini doğrulamak için kullanılan doğrulama kurallarını da bildirir. Kaynak kodu CreateTodoItemFormDTO sınıf aşağıdaki gibi görünür:


import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;

public class CreateTodoItemFormDTO {

    @Size(max = 1000)
    private String description;

    @NotBlank
    @Size(max = 100)
    private String title;

    //Getters and setters are omitted
}

Ardından, test edilen sistem tarafından döndürülen yanıt için nasıl iddia yazabileceğimizi öğreneceğiz.

Test Edilen Sistem Tarafından Döndürülen Yanıt için Beyan Yazma

Bir form gönderen bir Spring MVC denetleyicisi için birim testleri yazmadan önce, test edilen sistem tarafından döndürülen yanıt için iddiaları nasıl yazabileceğimizi öğrenmeliyiz. Test edilen Spring MVC denetleyicisi tarafından döndürülen yanıt için iddialar yazmak istediğimizde, bunları kullanmalıyız. static yöntemleri MockMvcResultMatchers sınıf:

  • bu status() yöntem bir döndürür StatusResultMatchers döndürülen HTTP durumu için iddialar yazmamıza izin veren nesne.
  • bu view() yöntem bir döndürür ViewResultMatchers işlenmiş görünüm için iddialar yazmamıza izin veren nesne.
  • bu model() yöntem bir döndürür ModelResultMatchers Spring MVC modeli için iddialar yazmamıza izin veren nesne.
  • bu flash() yöntem bir döndürür FlashAttributeResultMatchers kullanıcıya gösterilen flash öznitelikleri (diğer adıyla flash mesajlar) için iddialar yazmamıza izin veren nesne.

Artık test edilen sistem için birim testleri yazmaya hazırız. Yeni bir istek oluşturucu yöntemi yazarak başlayalım.

Yeni İstek Oluşturucu Yöntemi Yazma

Test sınıfımızdan yinelenen kodu kaldırmak istediğimiz için, istek oluşturucu sınıfı olarak adlandırılan bir sınıf kullanarak test edilen sisteme HTTP istekleri oluşturmalı ve göndermeliyiz. Başka bir deyişle, test edilen sistem için birim testleri yazmadan önce, test edilen sisteme HTTP istekleri gönderen bir istek oluşturucu yöntemine yazmalıyız. Bu istek oluşturucu yöntemini aşağıdaki adımları izleyerek yazabiliriz:

Öncelikleadlı yeni bir yöntem eklemeliyiz create() istek oluşturucu sınıfımıza. Bu yöntem bir CreateTodoItemFormDTO nesneyi bir yöntem parametresi olarak döndürür ve bir ResultActions nesne.

Bu metodu request builder sınıfımıza ekledikten sonra kaynak kodu şu şekilde görünür:


import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;

class TodoItemRequestBuilder {

    private final MockMvc mockMvc;

    TodoItemRequestBuilder(MockMvc mockMvc) {
        this.mockMvc = mockMvc;
    }

    ResultActions create(CreateTodoItemFormDTO formObject) throws Exception {
        
    }
}

İkinciuygulamak zorundayız create() şu adımları izleyerek yöntem:

  1. Gönder POST yola istek: ‘/todo-item’ perform() yöntemi MockMvc sınıf. iade etmeyi unutmayın ResultActions tarafından döndürülen nesne perform() yöntem.
  2. Gönderilen formun alan değerlerini aşağıdakileri kullanarak yapılandırın: param() yöntemi MockHttpServletRequestBuilder sınıf.

biz uyguladıktan sonra create() yöntemi, istek oluşturucu sınıfımızın kaynak kodu aşağıdaki gibi görünür:


import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;

class TodoItemRequestBuilder {

    private final MockMvc mockMvc;

    TodoItemRequestBuilder(MockMvc mockMvc) {
        this.mockMvc = mockMvc;
    }
    
    ResultActions create(CreateTodoItemFormDTO formObject) throws Exception {
        return mockMvc.perform(post("/todo-item")
                .param("description", formObject.getDescription())
                .param("title", formObject.getTitle())
        );
    }
}

Ardından, test edilen sistem için birim testleri yazmayı öğreneceğiz.

Test Edilen Sistem İçin Birim Testleri Yazma

Test edilen sistem için birim testleri yazmak istediğimizde şu adımları izlemeliyiz:

Öncelikle, gerekli sınıf hiyerarşisini test sınıfımıza eklemeliyiz. Birim testleri yazdığımız için aşağıdaki adımları izleyerek bu sınıf hiyerarşisini oluşturabiliriz:

  1. adlı bir iç sınıf ekleyin SubmitFormThatCreatesNewTodoItems test sınıfımıza. Bu iç sınıf, test edilen sistemin beklendiği gibi çalışmasını sağlayan test yöntemlerini içerir.
  2. adlı bir iç sınıf ekleyin WhenValidationFails için SubmitFormThatCreatesNewTodoItems sınıf. Bu iç sınıf, doğrulama başarısız olduğunda test edilen sistemin beklendiği gibi çalışmasını sağlayan test yöntemlerini içerir.
  3. adlı bir iç sınıf ekleyin WhenValidationIsSuccessful için SubmitFormThatCreatesNewTodoItems sınıf. Bu iç sınıf, doğrulama başarılı olduğunda test edilen sistemin beklendiği gibi çalışmasını sağlayan test yöntemlerini içerir.

Test sınıfımıza gerekli sınıf hiyerarşisini ekledikten sonra kaynak kodu şu şekilde görünür:


import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.springframework.context.support.StaticMessageSource;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static net.petrikainulainen.springmvctest.junit5.web.WebTestConfig.exceptionResolver;
import static net.petrikainulainen.springmvctest.junit5.web.WebTestConfig.fixedLocaleResolver;
import static net.petrikainulainen.springmvctest.junit5.web.WebTestConfig.jspViewResolver;
import static org.mockito.Mockito.mock;

public class TodoItemCrudControllerTest {

    private StaticMessageSource messageSource = new StaticMessageSource();
    private TodoItemRequestBuilder requestBuilder;
    private TodoItemCrudService service;

    @BeforeEach
    void configureSystemUnderTest() {
        service = mock(TodoItemCrudService.class);

        MockMvc mockMvc = MockMvcBuilders
                .standaloneSetup(new TodoItemCrudController(messageSource, service))
                .setHandlerExceptionResolvers(exceptionResolver())
                .setLocaleResolver(fixedLocaleResolver())
                .setViewResolvers(jspViewResolver())
                .build();
        requestBuilder = new TodoItemRequestBuilder(mockMvc);
    }

    @Nested
    @DisplayName("Submit the create todo item form")
    class SubmitCreateTodoItemForm {

        @Nested
        @DisplayName("When validation fails")
        class WhenValidationFails {

        }

        @Nested
        @DisplayName("When validation is successful")
        class WhenValidationIsSuccessful {

        }
    }
}

İkinciaşağıdaki değişiklikleri yapmalıyız SubmitFormThatCreatesNewTodoItems sınıf:

  1. Bulunan test yöntemleri tarafından kullanılan sabitleri bildirin. WhenValidationFails ve WhenValidationIsSuccessful iç sınıflar.
  2. Ekle private alana SubmitFormThatCreatesNewTodoItems sınıf. Bu alana denir formObject ve oluşturulan yapılacaklar öğesinin bilgilerini içerir.

Bu değişiklikleri yaptıktan sonra class, the source code of our test class looks as follows:


import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.springframework.context.support.StaticMessageSource;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static net.petrikainulainen.springmvctest.junit5.web.WebTestConfig.exceptionResolver;
import static net.petrikainulainen.springmvctest.junit5.web.WebTestConfig.fixedLocaleResolver;
import static net.petrikainulainen.springmvctest.junit5.web.WebTestConfig.jspViewResolver;
import static org.mockito.Mockito.mock;

public class TodoItemCrudControllerTest {

    private StaticMessageSource messageSource = new StaticMessageSource();
    private TodoItemRequestBuilder requestBuilder;
    private TodoItemCrudService service;

    @BeforeEach
    void configureSystemUnderTest() {
        service = mock(TodoItemCrudService.class);

        MockMvc mockMvc = MockMvcBuilders
                .standaloneSetup(new TodoItemCrudController(messageSource, service))
                .setHandlerExceptionResolvers(exceptionResolver())
                .setLocaleResolver(fixedLocaleResolver())
                .setViewResolvers(jspViewResolver())
                .build();
        requestBuilder = new TodoItemRequestBuilder(mockMvc);
    }

    @Nested
    @DisplayName("Submit the create todo item form")
    class SubmitCreateTodoItemForm {

        private static final String FORM_OBJECT_ALIAS = "todoItem";
        private static final int MAX_LENGTH_DESCRIPTION = 1000;
        private static final int MAX_LENGTH_TITLE = 100;

        private CreateTodoItemFormDTO formObject;

        @Nested
        @DisplayName("When validation fails")
        class WhenValidationFails {

        }

        @Nested
        @DisplayName("When validation is successful")
        class WhenValidationIsSuccessful {

        }
    }
}

Third, we have to ensure that the system under test is working as expected when validation fails. We can write the required test methods by following these steps:

  1. Add the required constants to the WhenValidationFails sınıf.
  2. için yeni bir kurulum yöntemi ekleyin. WhenValidationFails sınıf ve bir test yöntemi çalıştırılmadan önce çalıştırıldığından emin olun. Bu yöntemi uyguladığımızda, test yöntemlerimiz tarafından kullanılan form nesnesini oluşturmalıyız. Boş bir form gönderildiğinde test edilen sistemin beklendiği gibi çalıştığından emin olmak istediğimiz için yeni bir form oluşturmamız gerekiyor. CreateTodoItemFormDTO boş bir başlığı ve açıklaması olan nesne.
  3. Test edilen sistemin 200 HTTP durum kodunu döndürdüğünden emin olun.
  4. Test edilen sistemin form görünümünü oluşturduğunu doğrulayın.
  5. Test edilen sistemin boş bir yapılacaklar öğesi oluştur formu görüntülediğinden emin olun.
  6. Test edilen sistemin bir doğrulama hatası gösterdiğini doğrulayın.
  7. Test edilen sistemin boş bir başlık hakkında bir doğrulama hatası gösterdiğinden emin olun.
  8. Test edilen sistemin yeni bir yapılacaklar öğesi oluşturmadığını doğrulayın.

Gerekli test yöntemlerini yazdıktan sonra test sınıfımızın kaynak kodu şu şekilde görünüyor:


import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.StaticMessageSource;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static net.petrikainulainen.springmvctest.junit5.web.WebTestConfig.exceptionResolver;
import static net.petrikainulainen.springmvctest.junit5.web.WebTestConfig.fixedLocaleResolver;
import static net.petrikainulainen.springmvctest.junit5.web.WebTestConfig.jspViewResolver;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.emptyString;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.is;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;

public class TodoItemCrudControllerTest {

    private StaticMessageSource messageSource = new StaticMessageSource();
    private TodoItemRequestBuilder requestBuilder;
    private TodoItemCrudService service;

    @BeforeEach
    void configureSystemUnderTest() {
        service = mock(TodoItemCrudService.class);

        MockMvc mockMvc = MockMvcBuilders
                .standaloneSetup(new TodoItemCrudController(messageSource, service))
                .setHandlerExceptionResolvers(exceptionResolver())
                .setLocaleResolver(fixedLocaleResolver())
                .setViewResolvers(jspViewResolver())
                .build();
        requestBuilder = new TodoItemRequestBuilder(mockMvc);
    }

    @Nested
    @DisplayName("Submit the create todo item form")
    class SubmitCreateTodoItemForm {

        private static final String FORM_OBJECT_ALIAS = "todoItem";
        private static final int MAX_LENGTH_DESCRIPTION = 1000;
        private static final int MAX_LENGTH_TITLE = 100;

        private CreateTodoItemFormDTO formObject;

        @Nested
        @DisplayName("When validation fails")
        class WhenValidationFails {

            private static final String FORM_FIELD_NAME_DESCRIPTION = "description";
            private static final String FORM_FIELD_NAME_TITLE = "title";

            private static final String VALIDATION_ERROR_NOT_BLANK = "NotBlank";

            private static final String VIEW_NAME_FORM_VIEW = "todo-item/create";

            @BeforeEach
            void createFormObject() {
                formObject = new CreateTodoItemFormDTO();
                formObject.setDescription("");
                formObject.setTitle("");
            }

            @Test
            @DisplayName("Should return the HTTP status code OK (200)")
            void shouldReturnHttpStatusCodeOk() throws Exception {
                requestBuilder.create(formObject)
                        .andExpect(status().isOk());
            }

            @Test
            @DisplayName("Should render the form view")
            void shouldRenderFormView() throws Exception {
                requestBuilder.create(formObject)
                        .andExpect(view().name(VIEW_NAME_FORM_VIEW));
            }

            @Test
            @DisplayName("Should display an empty create todo item form")
            void shouldDisplayEmptyCreateTodoItemForm() throws Exception {
                requestBuilder.create(formObject)
                        .andExpect(model().attribute(FORM_OBJECT_ALIAS, allOf(
                                hasProperty(
                                        FORM_FIELD_NAME_DESCRIPTION, 
                                        is(emptyString())
                                ),
                                hasProperty(
                                        FORM_FIELD_NAME_TITLE, 
                                        is(emptyString())
                                )
                        )));
            }

            @Test
            @DisplayName("Should display one validation error")
            void shouldDisplayOneValidationError() throws Exception {
                requestBuilder.create(formObject)
                        .andExpect(model().attributeErrorCount(FORM_OBJECT_ALIAS, 1));
            }

            @Test
            @DisplayName("Should display a validation error about empty title")
            void shouldDisplayValidationErrorAboutEmptyTitle() throws Exception {
                requestBuilder.create(formObject)
                        .andExpect(model().attributeHasFieldErrorCode(
                                FORM_OBJECT_ALIAS,
                                FORM_FIELD_NAME_TITLE,
                                VALIDATION_ERROR_NOT_BLANK
                        ));
            }

            @Test
            @DisplayName("Shouldn't create a new todo item")
            void shouldNotCreateNewTodoItem() throws Exception {
                requestBuilder.create(formObject);
                verify(service, never()).create(any());
            }
        }

        //The other inner class is omitted
    }
}
Form nesnesinin doğrulaması şu durumlarda başarısız olur:

  • Oluşturulan yapılacaklar öğesinin başlığı null
  • Oluşturulan yapılacaklar öğesinin başlığı boş.
  • Oluşturulan yapılacaklar öğesinin başlığı yalnızca boşluk karakterlerini içerir.
  • Oluşturulan yapılacaklar öğesinin başlığı veya açıklaması çok uzun.

Bu blog yazısı sadece bir senaryoyu kapsıyor çünkü kendimi tekrarlamak istemedim. Test edilen sistemin tüm olası senaryolarda beklendiği gibi çalışmasını sağlayan testlere göz atmak isterseniz, bu blog gönderisinin örnek uygulamasına bir göz atın.

Dördüncü, doğrulama başarılı olduğunda test edilen sistemin beklendiği gibi çalıştığından emin olmalıyız. Aşağıdaki adımları takip ederek gerekli test yöntemlerini yazabiliriz:

  1. Gerekli sabitleri ekleyin WhenValidationIsSuccessful sınıf.
  2. için yeni bir kurulum yöntemi ekleyin. WhenValidationIsSuccessful sınıf ve bir test yöntemi çalıştırılmadan önce çalıştırıldığından emin olun. Bu yöntemi uyguladığımızda şunları yapmalıyız:
    • Geçerli bir başlığı ve açıklaması olan bir form nesnesi oluşturun.
    • Kullanıcıya gösterilen geri bildirim mesajını yapılandırın.
    • emin olun create() yöntemi TodoItemCrudService class, oluşturulan yapılacaklar öğesinin bilgilerini döndürür.
  3. Test edilen sistemin 302 HTTP durum kodunu döndürdüğünü doğrulayın.
  4. Test edilen sistemin HTTP isteğini yapılacaklar görünümü görünümüne yönlendirdiğinden emin olun.
  5. Test edilen sistemin doğru flaş mesajını görüntülediğini doğrulayın.
  6. Test edilen sistemin doğru açıklama ile yeni bir yapılacaklar öğesi oluşturduğundan emin olun.
  7. Test edilen sistemin doğru başlıkla yeni bir yapılacaklar öğesi oluşturduğunu doğrulayın.

Gerekli test yöntemlerini yazdıktan sonra test sınıfımızın kaynak kodu şu şekilde görünüyor:


import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.StaticMessageSource;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static info.solidsoft.mockito.java8.AssertionMatcher.assertArg;
import static net.petrikainulainen.springmvctest.junit5.web.WebTestConfig.exceptionResolver;
import static net.petrikainulainen.springmvctest.junit5.web.WebTestConfig.fixedLocaleResolver;
import static net.petrikainulainen.springmvctest.junit5.web.WebTestConfig.jspViewResolver;
import static net.petrikainulainen.springmvctest.junit5.web.WebTestUtil.createStringWithLength;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.flash;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;

public class TodoItemCrudControllerTest {

    private StaticMessageSource messageSource = new StaticMessageSource();
    private TodoItemRequestBuilder requestBuilder;
    private TodoItemCrudService service;

    @BeforeEach
    void configureSystemUnderTest() {
        service = mock(TodoItemCrudService.class);

        MockMvc mockMvc = MockMvcBuilders
                .standaloneSetup(new TodoItemCrudController(messageSource, service))
                .setHandlerExceptionResolvers(exceptionResolver())
                .setLocaleResolver(fixedLocaleResolver())
                .setViewResolvers(jspViewResolver())
                .build();
        requestBuilder = new TodoItemRequestBuilder(mockMvc);
    }

    @Nested
    @DisplayName("Submit the create todo item form")
    class SubmitCreateTodoItemForm {

        private static final String FORM_OBJECT_ALIAS = "todoItem";
        private static final int MAX_LENGTH_DESCRIPTION = 1000;
        private static final int MAX_LENGTH_TITLE = 100;

        private CreateTodoItemFormDTO formObject;

        //The other inner class is omitted

        @Nested
        @DisplayName("When validation is successful")
        class WhenValidationIsSuccessful {

            private static final String FEEDBACK_MESSAGE = "A new todo item was created";
            private static final String FEEDBACK_MESSAGE_KEY = "feedback.message.todoItem.created";

            private static final String FLASH_ATTRIBUTE_KEY_FEEDBACK_MESSAGE = "feedbackMessage";

            private static final String MODEL_ATTRIBUTE_NAME_ID = "id";
            private static final String VIEW_NAME_VIEW_TODO_ITEM_VIEW = "redirect:/todo-item/{id}";

            private static final Long ID = 1L;
            private static final String DESCRIPTION = createStringWithLength(MAX_LENGTH_DESCRIPTION);
            private static final String TITLE = createStringWithLength(MAX_LENGTH_TITLE);

            @BeforeEach
            void configureSystemUnderTest() {
                formObject = createFormObject();
                configureFeedbackMessage();
                returnCreatedTodoItem();
            }

            private CreateTodoItemFormDTO createFormObject() {
                CreateTodoItemFormDTO formObject = new CreateTodoItemFormDTO();
                formObject.setDescription(DESCRIPTION);
                formObject.setTitle(TITLE);
                return formObject;
            }

            private void configureFeedbackMessage() {
                messageSource.addMessage(
                        FEEDBACK_MESSAGE_KEY,
                        WebTestConfig.LOCALE,
                        FEEDBACK_MESSAGE
                );
            }

            private void returnCreatedTodoItem() {
                TodoItemDTO created = new TodoItemDTO();
                created.setId(ID);

                given(service.create(any())).willReturn(created);
            }

            @Test
            @DisplayName("Should return the HTTP status code found (302)")
            void shouldReturnHttpStatusCodeFound() throws Exception {
                requestBuilder.create(formObject)
                        .andExpect(status().isFound());
            }

            @Test
            @DisplayName("Should redirect the HTTP request to the view todo item view")
            void shouldRedirectHttpRequestToViewTodoItemView() throws Exception {
                requestBuilder.create(formObject)
                        .andExpect(view().name(VIEW_NAME_VIEW_TODO_ITEM_VIEW))
                        .andExpect(model().attribute(
                                MODEL_ATTRIBUTE_NAME_ID, equalTo(ID.toString())));
            }

            @Test
            @DisplayName("Should display the correct flash message")
            void shouldDisplayCorrectFlashMessage() throws Exception {
                requestBuilder.create(formObject)
                        .andExpect(flash().attribute(
                                FLASH_ATTRIBUTE_KEY_FEEDBACK_MESSAGE,
                                equalTo(FEEDBACK_MESSAGE)
                        ));
            }

            @Test
            @DisplayName("Should create a new todo item with the correct description")
            void shouldCreateNewTodoItemWithCorrectDescription() throws Exception {
                requestBuilder.create(formObject);

                verify(service, times(1)).create(assertArg(
                        todoItem -> assertThat(todoItem.getDescription())
                                .isEqualTo(DESCRIPTION)
                ));
            }

            @Test
            @DisplayName("Should create a new todo item with the correct title")
            void shouldCreateNewTodoItemWithCorrectTitle() throws Exception {
                requestBuilder.create(formObject);

                verify(service, times(1)).create(assertArg(
                        todoItem -> assertThat(todoItem.getTitle())
                                .isEqualTo(TITLE)
                ));
            }
        }
    }
}

Artık form gönderen bir controller metodu için birim testleri yazabiliriz. Bu blog gönderisinden öğrendiklerimizi özetleyelim.

Özet

Bu blog yazısı bize altı şey öğretti:

  • Gönderilen formun alan değerlerini kullanarak yapılandırabiliriz. param() yöntemi MockHttpServletRequestBuilder sınıf.
  • Test edilen sistemin X doğrulama hatası gösterdiğinden emin olmamız gerektiğinde, attributeErrorCount() yöntemi ModelResultMatchers sınıf.
  • Test edilen sistemin doğru doğrulama hatasını gösterdiğini doğrulamamız gerektiğinde, attributeHasFieldErrorCode() yöntemi ModelResultMatchers sınıf.
  • Oluşturulan formun alanlarının doğru bilgiler içerdiğinden emin olmamız gerektiğinde, attribute() yöntemi ModelResultMatchers sınıf.
  • HTTP isteğinin doğru yola yönlendirildiğini doğrulamamız gerektiğinde, name() yöntemi ViewResultMatchers sınıf.
  • Test edilen sistemin kullanıcıya doğru flaş mesajı gösterdiğinden emin olmamız gerektiğinde, attribute() yöntemi FlashAttributeResultMatchers sınıf.

Not: Yapabilirsiniz bu blog gönderisinin örnek uygulamasını Github’dan alın.

Bir cevap yazın

Ready to Grow Your Business?

We Serve our Clients’ Best Interests with the Best Marketing Solutions. Find out More

How Can We Help You?

Need to bounce off ideas for an upcoming project or digital campaign? Looking to transform your business with the implementation of full potential digital marketing?

For any career inquiries, please visit our careers page here.
[contact-form-7 404 "Bulunamadı"]