Spring Boot คือ ตอนที่ 9 : การทดสอบหน่วย (Unit Testing) และการทดสอบการรวมเข้าด้วยกัน (Integration Testing)

  1. การทดสอบหน่วย (Unit Testing) ใน Spring Boot
  2. การสร้างแอปพลิเคชัน
  3. การเขียนแบบทดสอบหน่วย (Unit Test)
  4. การทดสอบการรวมระบบ (Integration Testing)ใน Spring Boot
  5. การเขียนแบบทดสอบการบูรณาการ (Integration Testing)

การทดสอบเป็นส่วนสำคัญของกระบวนการการทำแอพใดๆ ช่วยให้มั่นใจได้ว่าโค้ดทำงานตามที่คาดไว้และป้องกันการเกิดข้อบกพร่องหรือการถดถอย ใน Spring Boot ซึ่งเป็นเฟรมเวิร์กที่มีคุณลักษณะหลากหลายซึ่งสร้างขึ้นจากเฟรมเวิร์ก Spring การทดสอบจะได้รับความสำคัญอย่างมากและได้รับการสนับสนุนด้วยยูทิลิตี้และไลบรารีในตัวหลายตัว

ในบทความนี้ เราจะเจาะลึกการทดสอบใน Spring Boot โดยเน้นที่การทดสอบหน่วยและการทดสอบการรวมระบบ นอกจากนี้ เราจะสาธิตแนวคิดเหล่านี้ด้วยการทำแอพ Spring Boot อย่างง่าย และเขียนการทดสอบสำหรับแอปพลิเคชันนั้น

1. การทดสอบหน่วย (Unit Testing) ใน Spring Boot

การทดสอบหน่วยเป็นวิธีการทดสอบโดยแยกแต่ละหน่วยของซอร์สโค้ด เช่น เมธอดหรือคลาส ใน Spring Boot เรามักจะใช้ JUnit ซึ่งเป็นเฟรมเวิร์กการทดสอบหน่วยยอดนิยมใน Java ร่วมกับ Mockito ซึ่งเป็นเฟรมเวิร์กจำลองที่เราใช้ในการเขียนการทดสอบหน่วยสำหรับเลเยอร์ที่มีการขึ้นต่อกันของเลเยอร์อื่น

2. การสร้างแอปพลิเคชัน

มาทำแอพ Spring Boot อย่างง่ายเพื่อสาธิตการทดสอบหน่วย เราจะพัฒนาบริการ RESTful พื้นฐานด้วยคลาสUserService ที่โต้ตอบกับฐานข้อมูลสมมุติโดยใช้อินเทอร์เฟซ UserRepository

@Service
public class UserService {
   
    @Autowired
    private UserRepository userRepository;
   
    public List<User> getAllUsers() {
        return userRepository.findAll();
    }
}

เป็นอินเทอร์เฟซ UserRepository ที่ขยาย Spring Data JPA’s JpaRepository.

public interface UserRepository extends JpaRepository<User, Long> {}

3. การเขียนแบบทดสอบหน่วย (Unit Test)

สำหรับคลาส UserService เราสนใจที่จะทดสอบวิธีการ getAllUsers นี้ เราจะเขียนการทดสอบเพื่อให้แน่ใจว่าวิธีนี้จะส่งคืนรายชื่อผู้ใช้เมื่อเรียก

@RunWith(SpringRunner.class)
public class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testGetAllUsers() {
        User user1 = new User(1L, "John Doe", "johndoe@example.com");
        User user2 = new User(2L, "Jane Doe", "janedoe@example.com");

        when(userRepository.findAll()).thenReturn(Arrays.asList(user1, user2));

        List<User> result = userService.getAllUsers();
        assertEquals(2, result.size());

        verify(userRepository, times(1)).findAll();
    }
}

ในการทดสอบนี้ เรากำลังจำลองอินเทอร์เฟซ UserRepository เนื่องจากการทดสอบหน่วยไม่ควรอาศัยการเชื่อมต่อฐานข้อมูลจริง เราใช้คำอธิบายประกอบ @Mock เพื่อสร้างวัตถุจำลองของคลาส UserRepository และคำอธิบายประกอบ @InjectMocks เพื่อใส่วัตถุจำลองลงในคลาส UserService ในเมธอด testGetAllUsers เราระบุลักษณะการทำงานของเมธอด userRepository.findAll() เมื่อเรียกเมธอดโดยใช้whenเมธอดของ Mockito จากนั้นยืนยันว่าเมธอด userService.getAllUsers() นั้นส่งคืนผลลัพธ์ที่คาดหวัง

4. การทดสอบการรวมระบบ (Integration Testing)ใน Spring Boot

ในขณะที่การทดสอบหน่วยตรวจสอบส่วนประกอบแต่ละรายการแยกกัน การทดสอบการรวมจะตรวจสอบว่าส่วนประกอบต่างๆ ของแอปพลิเคชันทำงานร่วมกันอย่างถูกต้อง เช่น การทำงานร่วมกันระหว่างตรรกะทางธุรกิจและชั้นฐานข้อมูล

ใน Spring Boot โดยทั่วไป เราจะใช้คำอธิบายประกอบ @SpringBootTest เพื่อแสดงคลาสการทดสอบการรวมระบบ คำอธิบายประกอบนี้โหลดบริบท ของแอปพลิเคชันที่สมบูรณ์สำหรับการทดสอบ และมักจะรวมกับการตั้งค่าสภาพแวดล้อมเว็บผ่าน WebEnvironment.RANDOM_PORT หรือ WebEnvironment.DEFINED_PORT

เราจะใช้ TestRestTemplate ซึ่งเป็นทางเลือก RestTemplate ที่เหมาะสำหรับการทดสอบการรวมระบบ

5. การเขียนแบบทดสอบการบูรณาการ (Integration Testing)

ในการสาธิต สมมติว่าเรามีคลาส UserController ที่มีเมธอด getAllUsers ที่แมปกับคำขอ GET

@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping
    public ResponseEntity<List<User>> getAllUsers() {
        return new ResponseEntity<>(userService.getAllUsers(), HttpStatus.OK);
    }
}

นี่คือวิธีที่เราสามารถเขียนการทดสอบการรวมสำหรับมัน:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class UserControllerIntegrationTest {

    @Autowired
    private TestRestTemplate restTemplate;

    @MockBean
    private UserService userService;

    @Test
    public void testGetAllUsers() {
        User user1 = new User(1L, "John Doe", "johndoe@example.com");
        User user2 = new User(2L, "Jane Doe", "janedoe@example.com");

        when(userService.getAllUsers()).thenReturn(Arrays.asList(user1, user2));

        ResponseEntity<List> response = restTemplate.getForEntity("/users", List.class);

        assertEquals(HttpStatus.OK, response.getStatusCode());
        assertEquals(2, response.getBody().size());
    }
}

ในการทดสอบด้านบน เรากำลัง TestRestTemplate ใช้คำขอ GET และ UserService ถูก mocked คำอธิบายประกอบ @MockBean จะใช้แทนคำอธิบายประกอบ @Mock ที่ใช้ในการทดสอบหน่วย คำอธิบายประกอบ @MockBean จะเพิ่มวัตถุจำลองในบริบทของแอปพลิเคชัน Spring โดยแทนที่ bean ที่มีอยู่ที่เป็นประเภทเดียวกันในบริบท


ทั้งการทดสอบหน่วยและการทดสอบการรวมมีบทบาทสำคัญในการรักษาความสมบูรณ์ของโค้ดเบสในการทำแอพ ในขณะที่การทดสอบหน่วยช่วยให้มั่นใจว่าส่วนประกอบแต่ละส่วนทำงานได้ตามที่คาดไว้ การทดสอบการรวมระบบจะยืนยันว่าส่วนประกอบเหล่านี้ทำงานร่วมกันได้ดี เมื่อใช้การสนับสนุนการทดสอบในตัวของ Spring Boot คุณสามารถเขียนการทดสอบที่มีประสิทธิภาพซึ่งเพิ่มความเสถียรและความน่าเชื่อถือของการทำแอพของคุณ


Spring Boot คืออะไร

Spring Boot คือ ตอนที่ 8 : การจัดการข้อยกเว้น (Exception Handling)
Spring Boot คือ ตอนที่ 10 : การรับรองความถูกต้อง (Authentication) และการอนุญาต (Authorization)