พัฒนา Spring MVC แบบ TDD ตอนที่ 2

ในตอนที่ 1 ผมได้พาเตรียมเครื่องมือสำหรับลุย TDD กันแล้ว ในตอนที่ 2 นี้ก็มาตามสัญญา เราจะมาลุย Test แรกกัน (ใครทำตามก่อนหน้านี้รบกวนกลับไปดูใหม่ด้วยนะครับ ผมมีแก้ไขเรื่อง dependency นิดหน่อย) ตอนที่ 2 นี้อาจจะดูเหมือนยาว แต่ไม่เยอะอย่างที่คิดครับ เนื่องจากเป็นขั้นตอนของการ Setup Spring Framework ทำให้เสียเวลาไปพอสมควร ในตอนต่อไปน่าจะสั้นลงกว่านี้ครับ

Register Tests


  1. สร้างคลาส RegisterControllerTests ไว้ใน src/test/java

    แก้ไขคลาสนี้เพื่อเตรียมพร้อมสำหรับการจำลอง Spring MVC และเพิ่มเมธอด Test แรกดังนี้

    package com.magicalcyber.minifacebook.web.register;
    
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
    import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.flash;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
    
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.springframework.test.context.web.WebAppConfiguration;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    import org.springframework.web.context.WebApplicationContext;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @WebAppConfiguration
    @ContextConfiguration("classpath:spring/test-mvc-core-config.xml")
    public class RegisterControllerTests {
    
    	@Autowired
    	private WebApplicationContext context;
    
    	private MockMvc mockMvc;
    
    	@Before
    	public void setup() {
    		this.mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
    	}
    
    	@Test
    	public void registerNonMemberShouldSuccess() throws Exception {
    		this.mockMvc
    				.perform(
    						post("/register").param("email", "magic@gmail.com")
    								.param("firstname", "magical")
    								.param("lastname", "cyber")
    								.param("password", "1234"))
    				.andDo(print())
    				.andExpect(redirectedUrl("/register"))
    				.andExpect(
    						flash().attribute("message", "Register successfully."));
    	}
    }
    

    ในเมธอด registerNonMemberShouldSuccess
    ผมจำลองให้มีการเรียก url /register โดย http method แบบ post
    พร้อมส่ง parameter ของ form หน้า registerไปด้วย

    จากนั้นให้ทำการ print ออกหน้าจอ console ว่ามีการ request/response อย่างไรบ้าง
    และสุดท้ายคือการตรวจสอบว่า

    มีการ redirect ไป /register หรือไม่

    และ

    ตัวแปร message ที่ scope ระดับ flash ว่ามีค่าเป็น "Register successfully." หรือไม่

  2. คลิกขวาที่คลาส RegisterControllerTests แล้วเลือก Run As -> JUnit Test
    แล้วก็ตู้มมมม JUnit ไม่ผ่านเนื่องจากหาไฟล์ config ไม่เจอ

    Caused by: java.io.FileNotFoundException: class path resource [spring/test-mvc-core-config.xml] cannot be opened because it does not exist
    at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:157)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:328)

  3. สร้างไฟล์ xml ชื่อ mvc-core-config.xml ไว้ใน /src/main/resources/spring/
    โดยข้างในมีดังนี้

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns:context="http://www.springframework.org/schema/context"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans
    						http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    						http://www.springframework.org/schema/mvc
    						http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
    						http://www.springframework.org/schema/context
    						http://www.springframework.org/schema/context/spring-context-3.2.xsd">
    
    	<context:component-scan base-package="com.magicalcyber.minifacebook.web" />
    
    	<mvc:annotation-driven />
    
    	<bean
    		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    		<property name="prefix" value="/WEB-INF/view/" />
    		<property name="suffix" value=".jsp" />
    	</bean>
    
    </beans>
    

    และสร้างไฟล์ xml ชื่อ test-mvc-core-config.xml ไว้ใน /src/test/resources/
    โดยข้างในมีดังนี้

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
    
        <import resource="classpath:spring/mvc-core-config.xml"/>
    
    </beans>
    

    ที่ต้องใช้วิธี import ตัว mvc-core-config.xml เข้ามาในตัว test เพื่อเป็นการลดความซ้ำซ้อนที่เราต้องคอยจัดการ config ถึงสองที่ ก็เลยใช้วิธี import แทนเพื่อให้เลือกที่ src/main/resources ที่เดียว

  4. สร้างโฟลเดอร์ WEB-INF/views ไว้ใน src/main/webapp เพื่อเก็บ jsp ตามที่ได้ระบุใน mvc-core-config.xml
  5. ยังครับ มันยังไม่จบ ให้สร้าง Controller ชื่อ RegisterController ใน src/main/java ดังนี้

    package com.magicalcyber.minifacebook.web.register;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.servlet.mvc.support.RedirectAttributes;
    
    @Controller
    @RequestMapping("/register")
    public class RegisterController {
    
    	@RequestMapping(method = RequestMethod.POST)
    	public String register(Model model, RedirectAttributes redirectAttrs) {
    		redirectAttrs.addFlashAttribute("message", "Register successfully.");
    		return "redirect:/register";
    	}
    }
    

    โค้ดเรียกได้ว่าเอาให้ผ่าน Test ให้ได้ก่อน

  6. กลับไปรัน Test อีกครั้ง คราวนี้ผ่านแล้ว!!!
    spring tdd - first test passed

ส่งท้าย

ถึงเราจะทดสอบผ่านในเบื้องต้น แต่อย่าลืมว่า เรายังไม่ได้ทำการ Register จริง คือยังไม่มีการบันทึกลงฐานข้อมูล และยังต้องเจอกับเคสอื่นๆ อีก อย่างเช่น email ซ้ำไม่สามารถสมัครได้ เป็นต้น

โปรดติดตามตอนต่อไปครับ

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s