ความแตกต่างระหว่างการใช้ MockMvc กับ SpringBootTest และการใช้ WebMvcTest


99

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

ข้อมูลโค้ด 1:

@RunWith(SpringRunner.class)
@WebMvcTest(HelloController.class)
public class HelloControllerApplicationTest {
    @Autowired    
    private MockMvc mvc;

    @Test
    public void getHello() throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("Greetings from Spring Boot!")));
    }
}

การทดสอบนี้ใช้@WebMvcTestคำอธิบายประกอบซึ่งฉันเชื่อว่ามีไว้สำหรับการทดสอบชิ้นส่วนคุณลักษณะและทดสอบเฉพาะเลเยอร์ MVC ของเว็บแอปพลิเคชัน

ข้อมูลโค้ด 2:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {

    @Autowired
    private MockMvc mvc;

    @Test
    public void getHello() throws Exception {
    mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(content().string(equalTo("Greetings from Spring Boot!")));
    }
}

การทดสอบนี้ใช้@SpringBootTestคำอธิบายประกอบและไฟล์MockMvc. แล้วสิ่งนี้แตกต่างจากข้อมูลโค้ด 1 อย่างไร? สิ่งนี้ทำแตกต่างกันอย่างไร?

แก้ไข: การเพิ่มข้อมูลโค้ด 3 (พบว่านี่เป็นตัวอย่างของการทดสอบการรวมในเอกสาร Spring)

@RunWith(SpringRunner.class) 
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 
public class HelloControllerIT {
    
    @LocalServerPort private int port;
    private URL base;
    
    @Autowired private TestRestTemplate template;
    
    @Before public void setUp() throws Exception {
        this.base = new URL("http://localhost:" + port + "/");
    }
    
    @Test public void getHello() throws Exception {
        ResponseEntity < String > response = template.getForEntity(base.toString(), String.class);
        assertThat(response.getBody(), equalTo("Greetings from Spring Boot!"));
    }
}

คำตอบ:


89

@SpringBootTestคือคำอธิบายประกอบการทดสอบทั่วไป หากคุณกำลังมองหาสิ่งที่ทำสิ่งเดียวกันก่อน 1.4 นั่นคือสิ่งที่คุณควรใช้ ไม่ใช้การแบ่งส่วนเลยซึ่งหมายความว่าจะเริ่มบริบทแอปพลิเคชันแบบเต็มของคุณและไม่ปรับแต่งการสแกนส่วนประกอบเลย

@WebMvcTestกำลังจะสแกนคอนโทรลเลอร์ที่คุณกำหนดไว้และโครงสร้างพื้นฐาน MVC เท่านั้น แค่นั้นแหละ. ดังนั้นหากคอนโทรลเลอร์ของคุณมีการพึ่งพาถั่วอื่น ๆ จากชั้นบริการของคุณการทดสอบจะไม่เริ่มต้นจนกว่าคุณจะโหลดการกำหนดค่านั้นด้วยตัวคุณเองหรือจำลองมันขึ้นมา ซึ่งเร็วกว่ามากเนื่องจากเราโหลดแอปของคุณเพียงบางส่วนเท่านั้น คำอธิบายประกอบนี้ใช้การแบ่งส่วน

การอ่านเอกสารก็น่าจะช่วยคุณได้เช่นกัน


ขอบคุณมากสำหรับการตอบกลับ !!. ดังนั้นถ้าฉันเข้าใจคุณถูกต้องความหมายก็คือข้อมูลโค้ดทั้งสองทดสอบเฉพาะส่วน MVC ของแอปพลิเคชัน แต่ข้อมูลโค้ด tcode 1 จะโหลดบริบทของแอปพลิเคชันแบบเต็มในขณะที่ข้อมูลโค้ด 2 จะสแกนเฉพาะคอนโทรลเลอร์เท่านั้น ถูกต้องหรือไม่ ข้อมูลโค้ด 1 ถือเป็นการทดสอบหน่วยเพื่อทดสอบคอนโทรลเลอร์ได้หรือไม่
Revansha

1
ไม่มันไม่ถูกต้อง SpringBootTestกำลังโหลดแอปพลิเคชันแบบเต็มของคุณ (ในบางส่วนขยายโดยค่าเริ่มต้นจะไม่เริ่มคอนเทนเนอร์แบบฝังหากมีอยู่นั่นคือสิ่งที่webEnvironmentมีไว้สำหรับ) ฉันจะไม่บอกว่านั่น@SpringBootTestเป็นการทดสอบหน่วยของคอนโทรลเลอร์ แต่เป็นการทดสอบการรวมมากกว่าจริงๆ WebMvcTestเป็นการทดสอบหน่วยของคอนโทรลเลอร์ของคุณในแง่ที่ว่าหากมีการพึ่งพาคุณจะต้องจัดเตรียมด้วยตัวเอง (ไม่ว่าจะเป็นการกำหนดค่าหรือการเยาะเย้ยบางประเภท)
Stephane Nicoll

ขอบคุณอีกครั้งสำหรับการตอบกลับ ฉันได้แก้ไขคำถามและเพิ่มข้อมูลโค้ด 3 คุณได้กล่าวว่าคำอธิบายประกอบ @SpringBootTest ถูกใช้มากขึ้นสำหรับการทดสอบการรวม ฉันเชื่อว่า Snippet 3 แสดงให้เห็นถึงสิ่งนี้ ดังนั้นหากการทดสอบการรวมทำเหมือนใน Snippet 3 แล้ว Snippet 2 จะทำอย่างไร Snippet 2 ใช้คำอธิบายประกอบ SpringBootTest และสภาพแวดล้อมจำลอง (ค่าเริ่มต้นของแอตทริบิวต์ wenEnvironment) นอกจากนี้ snippet 3 เริ่มเซิร์ฟเวอร์แบบฝังตัวและทำการเรียก HTTP จริงๆในขณะที่ snippet 2 ไม่ทำเช่นนี้ ดังนั้นเมื่อพิจารณาถึงสิ่งนี้แล้ว snippet 2 จะถือเป็นการทดสอบหน่วยไม่ได้?
Revansha

4
ฉันไม่แน่ใจว่าเราจะเรียงลำดับตรงนี้ อาจจะ gitter? สิ่งที่คุณดูเหมือนจะพลาดอยู่ตลอดเวลาคือบริบทของแอปพลิเคชันที่สร้างSpringBootTestและWebMvcTestแตกต่างกันอย่างมาก ก่อนหน้านี้โหลดแอป WHOLE ของคุณและเปิดใช้งานการกำหนดค่าอัตโนมัติทั้งหมดในขณะที่รุ่นหลังเปิดใช้งาน Spring Mvc เท่านั้นและไม่สแกนอะไรเลยHelloControllerนอกจาก ทั้งหมดขึ้นอยู่กับความหมายของการทดสอบหน่วยหลังจากทั้งหมด แต่นั่นคือความแตกต่าง
Stephane Nicoll

ขอบคุณสำหรับคำตอบของคุณ นั่นเป็นประโยชน์มากสำหรับฉัน ตอนนี้ฉันเข้าใจแล้วว่าทำไมการทดสอบของฉันจึงทำงานกับ SpringBootTest ได้ แต่มีข้อยกเว้นเมื่อ WebMvcTest ขอบคุณมากอีกครั้ง
Alps1992

71

คำอธิบายประกอบ@SpringBootTestบอกให้ Spring Boot ไปและค้นหาคลาสคอนฟิกูเรชันหลัก (เช่นเดียวกับ @SpringBootApplication เป็นต้น) และใช้เพื่อเริ่มบริบทแอ็พพลิเคชัน Spring SpringBootTest โหลดแอปพลิเคชั่นที่สมบูรณ์และฉีดถั่วทั้งหมดซึ่งอาจช้า

@WebMvcTest - สำหรับการทดสอบเลเยอร์คอนโทรลเลอร์และคุณต้องระบุการอ้างอิงที่เหลือที่จำเป็นโดยใช้ Mock Objects

คำอธิบายประกอบเพิ่มเติมด้านล่างสำหรับการอ้างอิงของคุณ

การทดสอบส่วนต่างๆของแอปพลิเคชัน บางครั้งคุณต้องการทดสอบ "สไลซ์" ง่ายๆของแอปพลิเคชันแทนที่จะกำหนดค่าแอปพลิเคชันทั้งหมดโดยอัตโนมัติ Spring Boot 1.4 ขอแนะนำ 4 คำอธิบายประกอบการทดสอบใหม่:

@WebMvcTest - for testing the controller layer
@JsonTest - for testing the JSON marshalling and unmarshalling
@DataJpaTest - for testing the repository layer
@RestClientTests - for testing REST clients

ดูข้อมูลเพิ่มเติม: https://spring.io/guides/gs/testing-web/


นี่คือการเชื่อมโยงไปBoot อ้างอิง Sping - คำอธิบายประกอบทดสอบการกำหนดค่าอัตโนมัติ มีมากกว่าสี่ @ roshankumar-mutha ที่ระบุไว้ที่นี่ ลิงก์ไปยังคู่มือเริ่มต้นใช้งานไม่ครอบคลุมส่วนต่างๆเหล่านี้ในเชิงลึก
George Pantazes

16

การทดสอบ MVC มีวัตถุประสงค์เพื่อครอบคลุมเฉพาะส่วนควบคุมของแอปพลิเคชันของคุณ คำขอและการตอบกลับของ HTTP ถูกล้อเลียนดังนั้นจึงไม่ได้สร้างการเชื่อมต่อที่แท้จริง ในทางกลับกันเมื่อคุณใช้@SpringBootTestการกำหนดค่าทั้งหมดสำหรับบริบทของเว็บแอปพลิเคชันจะถูกโหลดและการเชื่อมต่อจะผ่านเว็บเซิร์ฟเวอร์จริง ในกรณีนี้คุณไม่ได้ใช้ MockMvcbean แต่เป็นมาตรฐานRestTemplateแทน (หรือทางเลือกใหม่ TestRestTemplate)

เราควรเลือกอย่างใดอย่างหนึ่งเมื่อใด @WebMvcTestมีวัตถุประสงค์เพื่อทดสอบตัวควบคุมจากฝั่งเซิร์ฟเวอร์เป็นหน่วย @SpringBootTestในทางกลับกันควรใช้สำหรับการทดสอบการรวมเมื่อคุณต้องการโต้ตอบกับแอปพลิเคชันจากฝั่งไคลเอ็นต์

ไม่ได้หมายความว่าคุณไม่สามารถใช้ล้อเลียนกับ@SpringBootTest; หากคุณกำลังเขียนการทดสอบการรวมอาจยังจำเป็น ไม่ว่าในกรณีใดจะเป็นการดีกว่าที่จะไม่ใช้เพื่อทดสอบยูนิตของคอนโทรลเลอร์อย่างง่าย

แหล่งที่มา - การเรียนรู้ Microservices ด้วย Spring Boot


4
ฉันไม่เข้าใจว่าทำไมคำตอบนี้ถึงได้รับการโหวต .. เมื่อคุณใช้งาน@SpringBootTestเว็บเซิร์ฟเวอร์จริงจะไม่เริ่มทำงานเว้นแต่คุณจะมีwebEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT(หรือกDEFINED_PORT) และการเชื่อมต่อไม่ผ่านเว็บเซิร์ฟเวอร์จริง ค่าเริ่มต้นสำหรับมี@SpringBootTest WebEnvironment.MOCK
Koray Tugay
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.