สร้างด้วยความมั่นใจ: คู่มือการทดสอบ JUnit
เผยแพร่แล้ว: 2022-03-11ด้วยความก้าวหน้าของเทคโนโลยีและอุตสาหกรรมที่ย้ายจากโมเดล Waterfall เป็น Agile และตอนนี้เป็น DevOps การเปลี่ยนแปลงและการปรับปรุงในแอปพลิเคชันจะถูกนำไปใช้กับการผลิตในนาทีที่พวกเขาทำ ด้วยการปรับใช้โค้ดเพื่อการใช้งานจริงอย่างรวดเร็ว เราจึงต้องมั่นใจว่าการเปลี่ยนแปลงของเราได้ผล และไม่ทำลายฟังก์ชันการทำงานที่มีอยู่ก่อน
เพื่อสร้างความมั่นใจนี้ เราต้องมีกรอบสำหรับการทดสอบการถดถอยอัตโนมัติ สำหรับการทดสอบการถดถอย มีการทดสอบหลายอย่างที่ควรดำเนินการจากมุมมองระดับ API แต่ในที่นี้ เราจะครอบคลุมการทดสอบหลักๆ สองประเภท:
- การทดสอบหน่วย โดยที่การทดสอบที่กำหนดครอบคลุมหน่วยที่เล็กที่สุดของโปรแกรม (ฟังก์ชันหรือขั้นตอน) อาจใช้หรือไม่ใช้พารามิเตอร์อินพุตบางส่วนและอาจส่งคืนค่าบางอย่างหรือไม่ก็ได้
- การทดสอบการรวม โดยที่แต่ละหน่วยได้รับการทดสอบร่วมกันเพื่อตรวจสอบว่าหน่วยทั้งหมดโต้ตอบกันตามที่คาดไว้หรือไม่
มีเฟรมเวิร์กมากมายสำหรับทุกภาษาการเขียนโปรแกรม เราจะเน้นที่การเขียนหน่วยและการทดสอบการรวมสำหรับเว็บแอปที่เขียนในกรอบงาน Spring ของ Java
ส่วนใหญ่แล้ว เราเขียนเมธอดในคลาส และในทางกลับกัน สิ่งเหล่านี้จะโต้ตอบกับเมธอดของคลาสอื่น ในโลกปัจจุบัน—โดยเฉพาะอย่างยิ่งในแอปพลิเคชันระดับองค์กร—ความซับซ้อนของแอปพลิเคชันนั้นมากจนเมธอดเดียวอาจเรียกเมธอดมากกว่าหนึ่งวิธีจากหลายคลาส ดังนั้นเมื่อเขียน unit test สำหรับวิธีการดังกล่าว เราจำเป็นต้องมีวิธีการส่งคืนข้อมูลที่จำลองจากการเรียกเหล่านั้น เนื่องจากจุดประสงค์ของการทดสอบหน่วยนี้คือการทดสอบวิธีเดียวเท่านั้นและไม่ใช่การเรียกทั้งหมดที่วิธีนี้ทำ
ไปที่การทดสอบหน่วย Java ใน Spring โดยใช้เฟรมเวิร์ก JUnit เราจะเริ่มต้นด้วยสิ่งที่คุณอาจเคยได้ยิน: การเยาะเย้ย
การเยาะเย้ยคืออะไรและมันมาอยู่ในภาพเมื่อใด
สมมติว่าคุณมีคลาส CalculateArea
ซึ่งมีฟังก์ชัน calculateArea(Type type, Double... args)
ซึ่งคำนวณพื้นที่ของรูปร่างของประเภทที่กำหนด (วงกลม สี่เหลี่ยมจัตุรัส หรือสี่เหลี่ยมผืนผ้า)
รหัสมีลักษณะดังนี้ในแอปพลิเคชันปกติซึ่งไม่ได้ใช้การฉีดพึ่งพา:
public class CalculateArea { SquareService squareService; RectangleService rectangleService; CircleService circleService; CalculateArea(SquareService squareService, RectangleService rectangeService, CircleService circleService) { this.squareService = squareService; this.rectangleService = rectangeService; this.circleService = circleService; } public Double calculateArea(Type type, Double... r ) { switch (type) { case RECTANGLE: if(r.length >=2) return rectangleService.area(r[0],r[1]); else throw new RuntimeException("Missing required params"); case SQUARE: if(r.length >=1) return squareService.area(r[0]); else throw new RuntimeException("Missing required param"); case CIRCLE: if(r.length >=1) return circleService.area(r[0]); else throw new RuntimeException("Missing required param"); default: throw new RuntimeException("Operation not supported"); } } }
public class SquareService { public Double area(double r) { return r * r; } }
public class RectangleService { public Double area(Double r, Double h) { return r * h; } }
public class CircleService { public Double area(Double r) { return Math.PI * r * r; } }
public enum Type { RECTANGLE,SQUARE,CIRCLE; }
ตอนนี้ ถ้าเราต้องการทดสอบหน่วยของฟังก์ชัน calculateArea()
ของคลาส CalculateArea
แรงจูงใจของเราควรจะตรวจสอบว่า switch
เคสและเงื่อนไขข้อยกเว้นทำงานหรือไม่ เราไม่ควรทดสอบว่าบริการรูปร่างส่งคืนค่าที่ถูกต้องหรือไม่ เพราะดังที่ได้กล่าวไว้ก่อนหน้านี้ แรงจูงใจของการทดสอบหน่วยของฟังก์ชันคือการทดสอบตรรกะของฟังก์ชัน ไม่ใช่ตรรกะของการเรียกใช้ฟังก์ชัน
ดังนั้น เราจะจำลองค่าที่ส่งคืนโดยฟังก์ชันบริการแต่ละรายการ (เช่น rectangleService.area()
และทดสอบฟังก์ชันการเรียก (เช่น CalculateArea.calculateArea()
) ตามค่าจำลองเหล่านั้น
กรณีทดสอบอย่างง่ายสำหรับบริการสี่เหลี่ยมผืนผ้า การทดสอบที่ calculateArea()
เรียกใช้ rectangleService.area()
ด้วยพารามิเตอร์ที่ถูกต้อง จะมีลักษณะดังนี้:
import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; public class CalculateAreaTest { RectangleService rectangleService; SquareService squareService; CircleService circleService; CalculateArea calculateArea; @Before public void init() { rectangleService = Mockito.mock(RectangleService.class); squareService = Mockito.mock(SquareService.class); circleService = Mockito.mock(CircleService.class); calculateArea = new CalculateArea(squareService,rectangleService,circleService); } @Test public void calculateRectangleAreaTest() { Mockito.when(rectangleService.area(5.0d,4.0d)).thenReturn(20d); Double calculatedArea = this.calculateArea.calculateArea(Type.RECTANGLE, 5.0d, 4.0d); Assert.assertEquals(new Double(20d),calculatedArea); } }
สองบรรทัดหลักที่ควรทราบที่นี่คือ:
-
rectangleService = Mockito.mock(RectangleService.class);
—สิ่งนี้สร้างการเยาะเย้ย ซึ่งไม่ใช่วัตถุจริง แต่เป็นการล้อเลียน -
Mockito.when(rectangleService.area(5.0d,4.0d)).thenReturn(20d);
—นี่บอกว่าเมื่อจำลองและเรียกเมธอดarea
ของอ็อบเจ็กต์rectangleService
ด้วยพารามิเตอร์ที่ระบุ จากนั้นส่งคืน20d
จะเกิดอะไรขึ้นเมื่อโค้ดด้านบนเป็นส่วนหนึ่งของแอปพลิเคชัน Spring
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class CalculateArea { SquareService squareService; RectangleService rectangleService; CircleService circleService; public CalculateArea(@Autowired SquareService squareService, @Autowired RectangleService rectangeService, @Autowired CircleService circleService) { this.squareService = squareService; this.rectangleService = rectangeService; this.circleService = circleService; } public Double calculateArea(Type type, Double... r ) { // (same implementation as before) } }
ในที่นี้ เรามีคำอธิบายประกอบสองรายการสำหรับเฟรมเวิร์ก Spring พื้นฐานเพื่อตรวจหาในขณะที่เริ่มต้นบริบท:
-
@Component
: สร้าง bean ประเภทCalculateArea
-
@Autowired
: ค้นหา beanrectangleService
,squareService
และcircleService
แล้วใส่ลงใน bean ที่calculatedArea
ได้
ในทำนองเดียวกัน เราสร้าง bean สำหรับคลาสอื่นเช่นกัน:
import org.springframework.stereotype.Service; @Service public class SquareService { public Double area(double r) { return r*r; } }
import org.springframework.stereotype.Service; @Service public class CircleService { public Double area(Double r) { return Math.PI * r * r; } }
import org.springframework.stereotype.Service; @Service public class RectangleService { public Double area(Double r, Double h) { return r*h; } }
ตอนนี้ถ้าเราทำการทดสอบ ผลลัพธ์ก็เหมือนเดิม เราใช้ Constructor Injection ที่นี่ และโชคดีที่ไม่ต้องเปลี่ยนกรณีทดสอบของเรา
แต่มีอีกวิธีหนึ่งในการฉีดถั่วของบริการสี่เหลี่ยม วงกลม และสี่เหลี่ยมผืนผ้า: การฉีดภาคสนาม หากเราใช้สิ่งนั้น กรณีทดสอบของเราต้องมีการเปลี่ยนแปลงเล็กน้อย
เราจะไม่พูดถึงกลไกการฉีดที่ดีกว่า เนื่องจากไม่อยู่ในขอบเขตของบทความ แต่เราสามารถพูดแบบนี้ได้ ไม่ว่าคุณจะใช้กลไกแบบใดในการฉีด bean ก็มีวิธีเขียนการทดสอบ JUnit เสมอ
ในกรณีของการฉีดภาคสนาม โค้ดจะเป็นดังนี้:
@Component public class CalculateArea { @Autowired SquareService squareService; @Autowired RectangleService rectangleService; @Autowired CircleService circleService; public Double calculateArea(Type type, Double... r ) { // (same implementation as before) } }
หมายเหตุ: เนื่องจากเราใช้การแทรกฟิลด์ จึงไม่มีความจำเป็นสำหรับคอนสตรัคเตอร์แบบกำหนดพารามิเตอร์ ดังนั้นอ็อบเจกต์จะถูกสร้างขึ้นโดยใช้ค่าเริ่มต้นและค่าต่างๆ จะถูกตั้งค่าโดยใช้กลไกการแทรกฟิลด์
รหัสสำหรับคลาสบริการของเรายังคงเหมือนเดิม แต่รหัสสำหรับคลาสทดสอบมีดังนี้:
public class CalculateAreaTest { @Mock RectangleService rectangleService; @Mock SquareService squareService; @Mock CircleService circleService; @InjectMocks CalculateArea calculateArea; @Before public void init() { MockitoAnnotations.initMocks(this); } @Test public void calculateRectangleAreaTest() { Mockito.when(rectangleService.area(5.0d,4.0d)).thenReturn(20d); Double calculatedArea = this.calculateArea.calculateArea(Type.RECTANGLE, 5.0d, 4.0d); Assert.assertEquals(new Double(20d),calculatedArea); } }
มีบางสิ่งแตกต่างไปจากนี้: ไม่ใช่พื้นฐาน แต่เป็นวิธีที่เราบรรลุเป้าหมาย
ขั้นแรก วิธีที่เราจำลองวัตถุของเรา: เราใช้คำอธิบายประกอบ @Mock
ร่วมกับ initMocks()
เพื่อสร้างการเยาะเย้ย ประการที่สอง เราฉีด mocks ลงในวัตถุจริงโดยใช้ @InjectMocks
พร้อมกับ initMocks()
สิ่งนี้ทำเพื่อลดจำนวนบรรทัดของรหัส
นักวิ่งทดสอบคืออะไรและมีนักวิ่งประเภทใดบ้าง?
ในตัวอย่างข้างต้น รันเนอร์พื้นฐานที่ใช้ในการรันการทดสอบทั้งหมดคือ BlockJUnit4ClassRunner
ซึ่งตรวจจับคำอธิบายประกอบทั้งหมดและรันการทดสอบทั้งหมดตามลำดับ
หากเราต้องการฟังก์ชันเพิ่มเติม เราอาจเขียน Custom runner ตัวอย่างเช่น ในคลาสทดสอบข้างต้น หากเราต้องการข้ามบรรทัด MockitoAnnotations.initMocks(this);
เราก็สามารถใช้ runner อื่นที่สร้างขึ้นบน BlockJUnit4ClassRunner
ได้ เช่น MockitoJUnitRunner
เมื่อใช้ MockitoJUnitRunner
เราไม่จำเป็นต้องเริ่มต้นการเยาะเย้ยและฉีดเข้าไป นั่นจะทำโดย MockitoJUnitRunner
เพียงแค่อ่านคำอธิบายประกอบ
(นอกจากนี้ยังมี SpringJUnit4ClassRunner
ซึ่งเริ่มต้น ApplicationContext
ที่จำเป็นสำหรับการทดสอบการรวม Spring เช่นเดียวกับ ApplicationContext
ที่ถูกสร้างขึ้นเมื่อแอปพลิเคชัน Spring เริ่มทำงาน เราจะพูดถึงในภายหลัง)
การเยาะเย้ยบางส่วน
เมื่อเราต้องการให้อ็อบเจ็กต์ในคลาสทดสอบจำลองเมธอดบางเมธอด แต่ยังเรียกเมธอดจริงบางเมธอดด้วย เราก็จำเป็นต้องเยาะเย้ยบางส่วน สิ่งนี้ทำได้ผ่าน @Spy
ใน JUnit
ต่างจากการใช้ @Mock
กับ @Spy
วัตถุจริงจะถูกสร้างขึ้น แต่วิธีการของวัตถุนั้นสามารถล้อเลียนหรือสามารถเรียกได้จริง อะไรก็ได้ที่เราต้องการ
ตัวอย่างเช่น หากเมธอด area
ในคลาส RectangleService
เรียกใช้เมธอดพิเศษ log()
และเราต้องการพิมพ์บันทึกนั้นจริงๆ โค้ดจะเปลี่ยนเป็นดังนี้:

@Service public class RectangleService { public Double area(Double r, Double h) { log(); return r*h; } public void log() { System.out.println("skip this"); } }
หากเราเปลี่ยนคำอธิบายประกอบ @Mock
ของ rectangleService
Service เป็น @Spy
และทำการเปลี่ยนแปลงโค้ดดังที่แสดงด้านล่างในผลลัพธ์ เราจะเห็นว่าบันทึกถูกพิมพ์ แต่เมธอด area()
จะถูกเยาะเย้ย นั่นคือ ฟังก์ชันดั้งเดิมทำงานเพื่อผลข้างเคียงเท่านั้น ค่าที่ส่งคืนจะถูกแทนที่ด้วยค่าที่เยาะเย้ย
@RunWith(MockitoJUnitRunner.class) public class CalculateAreaTest { @Spy RectangleService rectangleService; @Mock SquareService squareService; @Mock CircleService circleService; @InjectMocks CalculateArea calculateArea; @Test public void calculateRectangleAreaTest() { Mockito.doCallRealMethod().when(rectangleService).log(); Mockito.when(rectangleService.area(5.0d,4.0d)).thenReturn(20d); Double calculatedArea = this.calculateArea.calculateArea(Type.RECTANGLE, 5.0d, 4.0d); Assert.assertEquals(new Double(20d),calculatedArea); } }
เราจะดำเนินการทดสอบ Controller
หรือตัว RequestHandler
ได้อย่างไร
จากสิ่งที่เราเรียนรู้ข้างต้น โค้ดทดสอบของคอนโทรลเลอร์สำหรับตัวอย่างของเราจะเป็นดังนี้:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class AreaController { @Autowired CalculateArea calculateArea; @RequestMapping(value = "api/area", method = RequestMethod.GET) @ResponseBody public ResponseEntity calculateArea( @RequestParam("type") String type, @RequestParam("param1") String param1, @RequestParam(value = "param2", required = false) String param2 ) { try { Double area = calculateArea.calculateArea( Type.valueOf(type), Double.parseDouble(param1), Double.parseDouble(param2) ); return new ResponseEntity(area, HttpStatus.OK); } catch (Exception e) { return new ResponseEntity(e.getCause(), HttpStatus.INTERNAL_SERVER_ERROR); } } }
import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @RunWith(MockitoJUnitRunner.class) public class AreaControllerTest { @Mock CalculateArea calculateArea; @InjectMocks AreaController areaController; @Test public void calculateAreaTest() { Mockito .when(calculateArea.calculateArea(Type.RECTANGLE,5.0d, 4.0d)) .thenReturn(20d); ResponseEntity responseEntity = areaController.calculateArea("RECTANGLE", "5", "4"); Assert.assertEquals(HttpStatus.OK,responseEntity.getStatusCode()); Assert.assertEquals(20d,responseEntity.getBody()); } }
เมื่อดูโค้ดทดสอบตัวควบคุมด้านบนแล้ว มันใช้งานได้ดี แต่มีปัญหาพื้นฐานอยู่อย่างหนึ่ง: มันทดสอบเฉพาะการเรียกเมธอด ไม่ใช่การเรียก API จริง กรณีทดสอบทั้งหมดที่พารามิเตอร์ API และสถานะของการเรียก API จำเป็นต้องทดสอบสำหรับอินพุตที่แตกต่างกันหายไป
รหัสนี้ดีกว่า:
import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; @RunWith(SpringJUnit4ClassRunner.class) public class AreaControllerTest { @Mock CalculateArea calculateArea; @InjectMocks AreaController areaController; MockMvc mockMvc; @Before public void init() { mockMvc = standaloneSetup(areaController).build(); } @Test public void calculateAreaTest() throws Exception { Mockito .when(calculateArea.calculateArea(Type.RECTANGLE,5.0d, 4.0d)) .thenReturn(20d); mockMvc.perform( MockMvcRequestBuilders.get("/api/area?type=RECTANGLE¶m1=5¶m2=4") ) .andExpect(status().isOk()) .andExpect(content().string("20.0")); } }
ที่นี่เราจะเห็นว่า MockMvc
ทำหน้าที่เรียก API จริงได้อย่างไร นอกจากนี้ยังมีตัวจับคู่พิเศษบางอย่าง เช่น status()
และ content()
ซึ่งทำให้ง่ายต่อการตรวจสอบความถูกต้องของเนื้อหา
การทดสอบการรวม Java โดยใช้ JUnit และ Mocks
ตอนนี้เรารู้แล้วว่าแต่ละหน่วยของโค้ดทำงานแล้ว มาทำให้แน่ใจว่าพวกมันโต้ตอบกันตามที่เราคาดไว้
อันดับแรก เราต้องยกตัวอย่างถั่วทั้งหมด ซึ่งเป็นสิ่งเดียวกันกับที่เกิดขึ้นในขณะที่การเริ่มต้นบริบทของ Spring ในระหว่างการเริ่มต้นแอปพลิเคชัน
สำหรับสิ่งนี้ เรากำหนด bean ทั้งหมดในคลาส สมมติว่า TestConfig.java
:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class TestConfig { @Bean public AreaController areaController() { return new AreaController(); } @Bean public CalculateArea calculateArea() { return new CalculateArea(); } @Bean public RectangleService rectangleService() { return new RectangleService(); } @Bean public SquareService squareService() { return new SquareService(); } @Bean public CircleService circleService() { return new CircleService(); } }
ตอนนี้เรามาดูกันว่าเราใช้คลาสนี้อย่างไรและเขียนการทดสอบการรวม:
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {TestConfig.class}) public class AreaControllerIntegrationTest { @Autowired AreaController areaController; MockMvc mockMvc; @Before public void init() { mockMvc = standaloneSetup(areaController).build(); } @Test public void calculateAreaTest() throws Exception { mockMvc.perform( MockMvcRequestBuilders.get("/api/area?type=RECTANGLE¶m1=5¶m2=4") ) .andExpect(status().isOk()) .andExpect(content().string("20.0")); } }
บางสิ่งเปลี่ยนแปลงที่นี่:
-
@ContextConfiguration(classes = {TestConfig.class})
— สิ่งนี้จะบอกกรณีการทดสอบที่มีคำจำกัดความ bean ทั้งหมดอยู่ - ตอนนี้แทนที่จะใช้
@InjectMocks
:
@Autowired AreaController areaController;
ทุกอย่างอื่นยังคงเหมือนเดิม หากเราดีบักการทดสอบ เราจะเห็นว่าโค้ดทำงานจริงจนถึงบรรทัดสุดท้ายของเมธอด area()
ใน RectangleService
โดยจะคำนวณ return r*h
กล่าวอีกนัยหนึ่ง ตรรกะทางธุรกิจที่แท้จริงทำงาน
นี่ไม่ได้หมายความว่าไม่มีการเยาะเย้ยของการเรียกเมธอดหรือการเรียกฐานข้อมูลในการทดสอบการรวม ในตัวอย่างข้างต้น ไม่มีการใช้บริการหรือฐานข้อมูลของบุคคลที่สาม ดังนั้นเราจึงไม่จำเป็นต้องใช้การจำลอง ในชีวิตจริง แอปพลิเคชันดังกล่าวหาได้ยาก และเรามักจะพบฐานข้อมูลหรือ API ของบุคคลที่สาม หรือทั้งสองอย่าง ในกรณีนั้น เมื่อเราสร้าง bean ในคลาส TestConfig
เราจะไม่สร้างอ็อบเจกต์จริง แต่สร้างอ็อบเจกต์จำลอง และใช้งานทุกที่ที่จำเป็น
โบนัส: วิธีสร้างข้อมูลการทดสอบวัตถุขนาดใหญ่
บ่อยครั้งสิ่งที่หยุดนักพัฒนาแบ็คเอนด์ในการเขียนหน่วยหรือการทดสอบการรวมคือข้อมูลการทดสอบที่เราต้องเตรียมสำหรับการทดสอบทุกครั้ง
โดยปกติ ถ้าข้อมูลมีขนาดเล็กเพียงพอ โดยมีตัวแปรหนึ่งหรือสองตัว ก็ง่ายที่จะสร้างออบเจ็กต์ของคลาสข้อมูลทดสอบและกำหนดค่าบางอย่าง
ตัวอย่างเช่น หากเราคาดหวังว่าวัตถุที่จำลองจะส่งคืนวัตถุอื่น เมื่อมีการเรียกใช้ฟังก์ชันบนวัตถุที่จำลอง เราจะทำสิ่งนี้:
Class1 object = new Class1(); object.setVariable1(1); object.setVariable2(2);
จากนั้นเพื่อใช้วัตถุนี้ เราจะทำสิ่งนี้:
Mockito.when(service.method(arguments...)).thenReturn(object);
นี่เป็นเรื่องปกติในตัวอย่าง JUnit ด้านบน แต่เมื่อตัวแปรสมาชิกในคลาส Class1
ด้านบนเพิ่มขึ้นเรื่อยๆ การตั้งค่าแต่ละฟิลด์จะกลายเป็นเรื่องยุ่งยาก บางครั้งมันอาจเกิดขึ้นที่คลาสมีสมาชิกคลาสอื่นที่ไม่ใช่คลาสดั้งเดิมที่กำหนดไว้ จากนั้น การสร้างออบเจ็กต์ของคลาสนั้นและการตั้งค่าฟิลด์ที่จำเป็นแต่ละรายการจะเพิ่มความพยายามในการพัฒนาเพียงเพื่อให้ต้นแบบสำเร็จ
วิธีแก้ไขคือสร้างสคีมา JSON ของคลาสข้างต้นและเพิ่มข้อมูลที่เกี่ยวข้องในไฟล์ JSON หนึ่งครั้ง ในคลาสทดสอบที่เราสร้างวัตถุ Class1
เราไม่จำเป็นต้องสร้างวัตถุด้วยตนเอง แต่เราอ่านไฟล์ JSON และใช้ ObjectMapper
แมปเข้ากับคลาส Class1
ที่ต้องการ:
ObjectMapper objectMapper = new ObjectMapper(); Class1 object = objectMapper.readValue( new String(Files.readAllBytes( Paths.get("src/test/resources/"+fileName)) ), Class1.class );
นี่เป็นความพยายามครั้งเดียวในการสร้างไฟล์ JSON และเพิ่มค่าให้กับไฟล์ การทดสอบใหม่ใดๆ หลังจากนั้นสามารถใช้สำเนาของไฟล์ JSON นั้นโดยมีการเปลี่ยนแปลงฟิลด์ตามความต้องการของการทดสอบใหม่
ข้อมูลเบื้องต้นเกี่ยวกับ JUnit: แนวทางที่หลากหลายและทักษะที่ถ่ายทอดได้
เห็นได้ชัดว่ามีหลายวิธีในการเขียนการทดสอบหน่วย Java ขึ้นอยู่กับว่าเราเลือกที่จะฉีดถั่วอย่างไร น่าเสียดายที่บทความส่วนใหญ่ในหัวข้อนี้มักจะคิดว่ามีเพียงวิธีเดียว ดังนั้นจึงง่ายที่จะสับสน โดยเฉพาะอย่างยิ่งเมื่อทำงานกับโค้ดที่เขียนขึ้นโดยใช้สมมติฐานที่ต่างออกไป หวังว่าแนวทางของเราที่นี่จะช่วยประหยัดเวลาของนักพัฒนาในการหาวิธีที่ถูกต้องในการเยาะเย้ยและตัวทดสอบใดที่จะใช้
โดยไม่คำนึงถึงภาษาหรือเฟรมเวิร์กที่เราใช้—อาจเป็นเวอร์ชันใหม่ของ Spring หรือ JUnit— ฐานแนวคิดยังคงเหมือนเดิมตามที่อธิบายไว้ในบทช่วยสอน JUnit ด้านบน มีความสุขในการทดสอบ!