Mock – ออปเจ็คท์หลอกๆ สำหรับใช้ทดสอบโปรแกรม

หลาย ๆ คนคงรู้จักคำว่า mock-up คำนี้มีความหมายว่า แบบจำลอง หรือวัตถุปลอม ๆ ที่เอาไว้สำหรับแสดงให้ลูกค้าดูว่าเมื่อผลิตภัณฑ์สร้างเสร็จแล้วหน้าตามันจะเป็นอย่างไร คำนี้มีเป็นคำผสมที่มีพื้นฐานจากคำว่า mock ที่แปลว่าการล้อเลียน

แล้วไอ้การล้อเลียนที่ว่านี้มันใช้ทำอะไรในการพัฒนาโปรแกรมได้ ?

คืองี้ครับ หลาย ๆ ครั้งที่เราต้องเขียนโปรแกรมที่ซับซ้อน และต้องอาศัย parameter จากคลาสที่มีความซับซ้อนมาก ๆ ซึ่งการทดสอบโปรแกรมนั้น ๆ ด้วยการสร้างตัวแปรขึ้นมาเป็น parameter ส่งเข้าไปนั้นอาจจะเป็นเรื่องที่ทำได้ยาก

อย่างเช่นสมมติว่าเราพัฒนาโปรแกรมที่เป็น Servlet โดยเรามีคลาสที่สืบทอดมาจาก HTTPServlet และเรากำลังจะเขียนโปรแกรมทดสอบเจ้าเมธอด doGet() ที่มีหน้าตาแบบนี้

class MyServlet extends HTTPServlet {
    @Override
    protected void doGet(HttpServletRequest req,
                 HttpServletResponse resp) {
    /* Implementations */
    }
}

คำถามคือ แล้วเราจะสร้างตัวแปรที่มีประเภทเป็น HttpServletRequest และ HttpServletResponse ได้อย่างไร ? เพราะเจ้า type ทั้งสองตัวนี้เป็น Interface ครับ

คำตอบน่ะเหรอ ... เขียนคลาสใหม่ขึ้นมา implement เจ้าสองคลาสนี้ไปเลยครับ

class MockHttpServletRequest implments HttpServletRequest{
     /* implements every method here*/
}

class MockHttpServletResponse implements HttpServletResponse {
    /* implements every method here*/
}

เขียน implementation ของแต่ละ method ข้างในให้ง่ายที่สุดครับ อันไหน get ก็คืนค่าคงที่ไปเลย อันไหน set ก็สร้างตัวแปรมารับ อะไรทำนองนี้ เช่น

class MockHttpServletRequest implments HttpServletRequest{
    String getContextPath() {
        return "/";
    }
}

ทั้งนี้ถ้าเป็น method ที่ใช้ในการทดสอบด้วย ก็ต้องเขียนให้สอดคล้องกับ test case ด้วยนะครับ

ทีนี้เราก็เขียนโปรแกรม unit test ได้ง่าย ๆ แบบนี้ครับ

class MyHttpServletTest {
    @Test
    void testDoGetRootPath() {
        HttpServletRequest req = new MockHttpServletRequest();
        HttpServletResponse resp = new MockHttpServletResponse();
        MyHttpServlet servlet = new MyHttpServlet();

        servlet.service(req, resp);
    }
}

อันนี้พอดีว่า doGet() มันเป็น private method ครับ ไปเรียกตรง ๆ ไม่ได้ ผมเลี่ยงไปเรียก service() แทน เพราะผมรู้ว่ามันจะไปเรียก doGet() แต่ถ้าอยากทดสอบเจ้า doGet() ก็มีอีกวิธี ก็คือเขียนชุดโปรแกรมทดสอบเป็น class ที่สืบทอดเจ้า MyHttpServletTest อีกต่อไปเลยโลด

class MyHttpServletTest extends MyHttpServlet{
    @Test
    void testDoGetRootPath() {
        HttpServletRequest req = new MockHttpServletRequest();
        HttpServletResponse resp = new MockHttpServletResponse();
        MyHttpServletTest servlet = new MyHttpServlet();

        servlet.doGet(req, resp);
    }
}

แค่นี้เอง

(อีกวิธีคือเอาตัว test ใส่ลงไปในคลาสที่จะ test ไปเลย ซึ่งไม่แนะนำครับ เพราะว่าตัว test จะติดไปในโค๊ดที่จะเอาไปรันจริงด้วย ทำให้มันกินแรมมากขึ้นเปล่า ๆ)

ปัญหาของการใช้ Mock คือ ... ในกรณีที่แย่ที่สุดเราอาจจะต้องสร้างคลาสขึ้นมาสำหรับแต่ละเทสต์เคสเลย ซึ่งมันเยอะมาก และมันก็อาจจะไม่ชัดเจนพอที่เอาไปใส่ใน unit test (ซึ่งควรจะเรียบง่ายที่สุดไม่งั้นมันจะอ่านยาก) ที่แย่กว่านั้นเราต้อง implement เมธอดที่เราไม่ได้ใช้ทดสอบด้วย ทำให้โค๊ดยาวโดยใช้เหตุ

สำหรับจุดนี้เราสามารถใช้ mock framework เข้ามาช่วยได้ครับ ในภาษา Java นั้นมี Mock Framework ที่ได้รับความนิยมอยู่หลายตัว (ลอง Google ดูนะครับ) ผมจะลองใช้ Mockito ดู เจ้าโปรแกรมทดสอบมันก็จะเหลือแค่นี้

import static org.mockito.Mockito.*;

class MyHttpServletTest extends MyHttpServlet{
    @Test
    void testDoGetRootPath() {
        HttpServletRequest req = mock(HttpServletRequest.class);
        when(req.getContextPath()).thenReturn("/");
        HttpServletResponse resp = mock(HttpServletResponse.class);
        MyHttpServletTest servlet = new MyHttpServlet();

        servlet.doGet(req, resp);
    }
}

แค่นี้เอง ผมไม่ต้องสร้างคลาสใหม่ขึ้นมาเพื่อใช้ทดสอบด้วย ยอดไปเลย :)

สุดท้ายนี้ถ้าถามว่าทำไมถึงเรียกว่า mock ผมคิดว่ามันเกิดจากการล้อเลียนเจ้า class จริง ๆ น่ะครับ เราสร้าง class ใหม่ขึ้นมาล้อเลียนตัวเดิมที่ใช้งานได้ กลายเป็นคลาสง่อย ๆ ตัวนึง อะไรทำนองนี้ ที่จริงการสร้าง mock-up ก็คือการสร้างวัตถุชิ้นหนึ่งที่มีหน้าตาเหมือนของจริงแต่ทำอะไรไม่ได้ ใช่ไหมล่ะครับ 😉 เจ้า mock object นี่ก็เหมือนกันแหละ

ปล.โค๊ดข้างบนยังไม่ได้เทสต์ครับ อาจจะมีจุดผิดได้ 555 ลองดูก็แล้วกัน

Wutipong Wongsakuldej

Programmer, interested in frontend applications, music and multimedia.

Latest posts by Wutipong Wongsakuldej (see all)

Leave a Reply

Your email address will not be published. Required fields are marked *