สร้างด้วยความมั่นใจ: คู่มือการทดสอบ 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 : ค้นหา bean rectangleService , 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&param1=5&param2=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&param1=5&param2=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 ด้านบน มีความสุขในการทดสอบ!