CodeGym /จาวาบล็อก /สุ่ม /การทดสอบหน่วยใน Java ด้วย JUnit
John Squirrels
ระดับ
San Francisco

การทดสอบหน่วยใน Java ด้วย JUnit

เผยแพร่ในกลุ่ม

การทดสอบหน่วยใน Java คืออะไร

ก่อนที่เราจะเรียนรู้เกี่ยวกับ JUnit ใน Java เรามาทำความเข้าใจคร่าวๆ กันก่อนว่า Unit Testing คืออะไร และเหตุใดจึงเป็นที่นิยม (หากคุณรู้เรื่องนี้อยู่แล้ว ให้ข้ามไปที่ 'ฉันจะเขียน JUnit Test ใน Java ได้อย่างไร') การทดสอบหน่วยใน Java ทำให้การพัฒนาซอฟต์แวร์ขนาดใหญ่มีประสิทธิภาพและง่ายดายมากขึ้น สามารถช่วยทั้งบุคคลและทีมลดเวลานับไม่ถ้วนในการดีบักและปรับปรุงกระบวนการทำงานร่วมกันอย่างมาก การทดสอบหน่วยใน Java ด้วย JUnit - 1

https://junit.org/junit4/

แนวคิดที่สำคัญของการทดสอบหน่วยคือ: เขียนการทดสอบอะตอมของคุณลักษณะแต่ละรายการ (เรียกว่าการทดสอบหน่วย) และค่อยๆ เพิ่มคุณลักษณะเพิ่มเติมหลังจากการทดสอบและตรวจสอบให้แน่ใจว่าการทดสอบก่อนหน้านี้ทำงานได้ เป็นแนวคิดที่เรียบง่ายแต่ทรงพลังอย่างยิ่ง เพื่อเป็นตัวอย่างว่ากระบวนการนี้จะมีลักษณะอย่างไร ลองจินตนาการว่าคุณกำลังสร้างเครื่องคำนวณทางวิทยาศาสตร์เสมือนจริง นอกเหนือจากตัวดำเนินการทางคณิตศาสตร์ที่ชัดเจน ( +, -, x, %) เครื่องคิดเลขนี้จะมีคุณสมบัติขั้นสูงที่ต้องใช้คุณสมบัติย่อยอื่นๆ ในการทำงาน ในการคำนวณเลขยกกำลัง เครื่องคิดเลขของคุณจะต้องสามารถคูณได้อย่างถูกต้อง ดังนั้นแนวทางการทดสอบหน่วยในการสร้างและทดสอบเครื่องคิดเลขนี้จะเป็น:
  • เขียนฟังก์ชันการบวก ทดสอบอย่างระมัดระวัง เปลี่ยน ทำซ้ำจนกว่าจะได้ผล
  • ทำเช่นเดียวกันกับฟังก์ชันการลบ การคูณ การหาร
  • ใช้ตัวดำเนินการฐานเหล่านี้เพื่อเขียนฟังก์ชันตัวดำเนินการขั้นสูง เช่น เลขยกกำลัง จากนั้นทดสอบฟังก์ชันเหล่านั้นด้วย
สิ่งนี้ทำให้มั่นใจได้ว่าคุณสมบัติที่สร้างจากคุณสมบัติย่อยที่เล็กกว่าอื่นๆ ไม่เพียงแต่ทำงานได้อย่างถูกต้องตามสิทธิของตนเองเท่านั้น แต่ไม่มีคุณสมบัติย่อยที่ผิดพลาดอยู่ภายใน ตัวอย่างเช่น ถ้าฉันกำลังทดสอบฟังก์ชันเลขชี้กำลังและมีบางอย่างผิดพลาด ฉันรู้ว่าข้อผิดพลาดนั้นไม่ได้อยู่ในคุณลักษณะย่อยของการคูณ เนื่องจากฟังก์ชันการคูณได้รับการทดสอบอย่างกว้างขวางแล้ว สิ่งนี้ช่วยลดจำนวนโค้ดทั้งหมดที่ฉันต้องใช้ในการย้อนรอยและตรวจสอบเพื่อหาจุดบกพร่องได้อย่างมาก หวังว่าตัวอย่างเล็กๆ น้อยๆ นี้จะทำให้ชัดเจนว่ากระบวนการคิดเกี่ยวกับการทดสอบหน่วยมีโครงสร้างอย่างไร แต่การทดสอบหน่วยโต้ตอบกับกระบวนการพัฒนาซอฟต์แวร์ที่เหลืออย่างไร จะเกิดอะไรขึ้นถ้าคุณมีฟีเจอร์ที่ซับซ้อนมากขึ้น ซึ่งจำเป็นต้องทำงานและสื่อสารร่วมกันได้ การทดสอบหน่วยไม่เพียงพอเพื่อให้แน่ใจว่าคุณสมบัติที่ซับซ้อนดังกล่าวสามารถทำงานร่วมกันได้อย่างเหมาะสม ที่จริงแล้วเป็นเพียงขั้นตอนแรกของการทดสอบซอฟต์แวร์สี่ระดับ (ฉันใช้ตัวพิมพ์ใหญ่เพราะฉันอ้างถึงมาตรฐานอุตสาหกรรมหรือแนวทางทั่วไปในการทดสอบซอฟต์แวร์) สามขั้นตอนสุดท้ายคือการทดสอบการรวมระบบการทดสอบระบบและการทดสอบการยอมรับ. ทั้งหมดนี้อาจหมายถึงสิ่งที่คุณคิดอย่างแน่นอน แต่ให้ฉันชี้แจง: การทดสอบการรวมเป็นสิ่งที่เราจะทำเพื่อให้มั่นใจว่า "คุณลักษณะที่ซับซ้อน" ที่กล่าวมาข้างต้นมีปฏิสัมพันธ์ซึ่งกันและกันอย่างเหมาะสม (เช่น ตรวจสอบให้แน่ใจว่าเครื่องคิดเลขสามารถรองรับ “3 + 7 * 4 - 2” ได้) การทดสอบระบบคือการทดสอบการออกแบบโดยรวมของระบบใดระบบหนึ่ง มักจะมีระบบหลายระบบที่มีฟีเจอร์ที่ซับซ้อนทำงานร่วมกันในผลิตภัณฑ์ ดังนั้นคุณจึงจัดกลุ่มเหล่านี้เป็นระบบต่างๆ และทดสอบทีละรายการ (เช่น หากคุณกำลังสร้างเครื่องคำนวณกราฟ ก่อนอื่นคุณต้องสร้าง 'ระบบ' เลขคณิตเพื่อจัดการกับตัวเลข ทดสอบจนกว่าจะทำงานได้ตามที่ตั้งใจ จากนั้นคุณจะต้องสร้างและทดสอบ 'ระบบ' กราฟเพื่อจัดการการถอน เช่น มันจะต่อยอดมาจากระบบเลขคณิต) การทดสอบการยอมรับคือการทดสอบระดับผู้ใช้ กำลังดูว่าระบบทั้งหมดสามารถทำงานประสานกันเพื่อสร้างผลิตภัณฑ์สำเร็จรูปที่พร้อมให้ผู้ใช้ยอมรับหรือไม่ (เช่น ผู้ใช้ทดสอบเครื่องคิดเลข) บางครั้งนักพัฒนาซอฟต์แวร์อาจเพิกเฉยต่อขั้นตอนสุดท้ายของกระบวนการนี้ เนื่องจากบริษัทต่างๆ มักจะมีพนักงานคนอื่นๆ ทำการทดสอบผู้ใช้ (เบต้า) แยกต่างหาก

ฉันจะเขียนการทดสอบ JUnit ใน Java ได้อย่างไร

ตอนนี้คุณมีความคิดที่ชัดเจนขึ้นเกี่ยวกับประโยชน์และข้อจำกัดของการทดสอบหน่วยแล้ว มาดูโค้ดบางส่วนกันดีกว่า! เราจะใช้เฟรมเวิร์กการทดสอบ Java ยอดนิยมที่เรียกว่า JUnit (อีกอันยอดนิยมคือ TestNG ซึ่งคุณสามารถใช้ได้หากต้องการ พวกมันคล้ายกันมาก ในรูปแบบวากยสัมพันธ์ TestNG ได้รับแรงบันดาลใจจาก JUnit) คุณสามารถดาวน์โหลดและติดตั้ง JUnit ได้ที่นี่ สำหรับโค้ดตัวอย่างนี้ เราจะดำเนินการต่อจากตัวอย่าง 'เครื่องคิดเลขวิทยาศาสตร์' ที่ฉันพูดถึงก่อนหน้านี้ มันค่อนข้างง่ายที่จะคาดคะเน และโค้ดทดสอบก็ง่ายสุดๆ แนวทางปฏิบัติทั่วไปคือการเขียนชั้นเรียนแบบทดสอบแยกต่างหากสำหรับแต่ละชั้นเรียนของคุณ ดังนั้นนั่นคือสิ่งที่เราจะทำ สมมติว่า ณ จุดนี้ เรามีMath.javaไฟล์ที่มีฟังก์ชันทางคณิตศาสตร์ทั้งหมดอยู่ในนั้น (รวมถึงMath.add) และเรากำลังเขียนMathTests.javaไฟล์ในแพ็คเกจเดียวกัน ตอนนี้มาตั้งค่าคำสั่งการนำเข้าและเนื้อหาของคลาส: (คำถามสัมภาษณ์ JUnit ที่เป็นไปได้: คุณอาจถูกถามว่าจะวางการทดสอบ JUnit ไว้ที่ใด และจำเป็นต้องนำเข้าไฟล์ต้นฉบับหรือไม่ หากคุณกำลังเขียนคลาสทดสอบในแพ็คเกจเดียวกันกับ คลาสหลักของคุณ คุณไม่จำเป็นต้องมีคำสั่งนำเข้าสำหรับไฟล์ต้นฉบับของคุณในคลาสทดสอบ มิฉะนั้น ตรวจสอบให้แน่ใจว่าคุณกำลังนำเข้าไฟล์ต้นฉบับของคุณ!)

import org.junit.jupiter.Test;    //gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; //less typing :) 

public class MathTests {
	//...
}
คำสั่งการนำเข้าแรกให้@Testส่วนหัว แก่เรา เราเขียน ' @Test' ไว้บนสุดของนิยามฟังก์ชันการทดสอบทั้งหมดโดยตรง เพื่อให้ JUnit รู้ว่านี่เป็นการทดสอบหน่วยเอกพจน์ที่สามารถรันแยกกันได้ ในภายหลัง ฉันจะแสดงให้คุณเห็นว่าคุณสามารถเรียกใช้การทดสอบเฉพาะหน่วยโดยใช้ส่วนหัวนี้ได้อย่างไร คำสั่งนำเข้าที่สองช่วยให้เราไม่ต้องพิมพ์เล็กน้อย ฟังก์ชัน JUnit หลักที่เราใช้ในการทดสอบฟังก์ชันของเราคือ to Assert.assertEquals()ซึ่งใช้พารามิเตอร์สองตัว (ค่าจริงและค่าที่คาดไว้) และตรวจสอบให้แน่ใจว่ามีค่าเท่ากัน การมีคำสั่งนำเข้าที่สองนี้ช่วยให้เราสามารถพิมพ์ " assertEquals(..." แทนที่จะต้องระบุทุกครั้งว่าเป็นส่วนหนึ่งของแพ็คเกจใด ทีนี้ลองเขียนกรณีทดสอบง่ายๆ เพื่อยืนยันว่า 2 + 2 เป็น 4 จริง ๆ !

import org.junit.jupiter.Test; // gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; // less typing :) 


public class MathTests {
	@Test
	public void add_twoPlusTwo_returnsFour(){
	final int expected = 4;
	final int actual = Math.add(2, 2);
	assertEquals(“2+2 is 4”, actual, expected);
	}
}
มาดูห้าบรรทัดของฟังก์ชันทดสอบแต่ละบรรทัดและสิ่งที่พวกเขาทำกัน: บรรทัดที่ 5: @Testส่วนหัวนี้ระบุว่าคำจำกัดความของฟังก์ชันด้านล่างadd_twoPlusTwo_returnsFour()เป็นฟังก์ชันทดสอบที่ JUnit สามารถเรียกใช้แยกกันได้ บรรทัดที่ 6: นี่คือลายเซ็นของฟังก์ชันสำหรับกรณีทดสอบของเรา กรณีทดสอบมักจะเป็นเอกพจน์เสมอ พวกเขาทดสอบเพียงหนึ่งตัวอย่างเฉพาะ เช่น 2+2=4 เป็นเรื่องปกติที่จะตั้งชื่อกรณีทดสอบของคุณในรูปแบบ " [function]_[params]_returns[expected]()," โดยที่[function]ชื่อของฟังก์ชันที่คุณกำลังทดสอบ[params]คือพารามิเตอร์ตัวอย่างเฉพาะที่คุณกำลังทดสอบ และ[expected]เป็นค่าส่งคืนที่คาดไว้ของฟังก์ชัน ฟังก์ชันการทดสอบมักจะมีประเภทการส่งคืนเป็น '' void' เสมอ เนื่องจากประเด็นหลักของฟังก์ชันทั้งหมดคือการเรียกใช้assertEqualsซึ่งจะส่งออกไปยังคอนโซลไม่ว่าการทดสอบของคุณจะผ่านหรือไม่ก็ตาม คุณไม่จำเป็นต้องส่งคืนข้อมูลอื่นใดที่ใดก็ได้ บรรทัดที่ 7: เราประกาศfinalตัวแปร ' ' ของประเภทการส่งคืนเป็นMath.add (int)และตั้งชื่อว่า 'คาดหวัง' ตามแบบแผน ค่าของมันคือคำตอบที่เราคาดหวัง (4) บรรทัดที่ 8: เราประกาศตัวแปร ' final' ของประเภทการส่งคืนเป็นMath.add (int)และตั้งชื่อว่า 'จริง' ตามแบบแผน ค่าของมันเป็นผลมาMath.add(2, 2)จาก บรรทัดที่ 9: เส้นสีทอง นี่คือบรรทัดที่เปรียบเทียบค่าจริงและค่าที่คาดไว้ และบอกเราว่าเราผ่านการทดสอบก็ต่อเมื่อมีค่าเท่ากัน พารามิเตอร์แรกที่ส่งผ่าน “2+2 is 4” คือคำอธิบายของฟังก์ชันการทดสอบ

จะเกิดอะไรขึ้นถ้าฟังก์ชันของฉันควรมีข้อยกเว้น

หากตัวอย่างการทดสอบเฉพาะของคุณควรแสดงข้อยกเว้นแทนที่จะยืนยันว่าค่าจริงและค่าที่คาดไว้เท่ากัน JUnit มีวิธีอธิบายสิ่งนี้ในส่วน@Testหัว ลองดูตัวอย่างด้านล่าง สมมติว่าเรามีฟังก์ชันในMath.javacall Math.divideเราต้องการให้แน่ใจว่าอินพุตไม่สามารถหารด้วย 0 ได้ แต่การพยายามเรียกMath.divide(a, 0)ใช้ค่า 'a' ใดๆ ควรมีข้อยกเว้น ( ArithmeticException.class) เราระบุไว้ในส่วนหัวดังนี้:

import org.junit.jupiter.Test; // gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; // less typing :) 


public class MathTests {
	@Test (expectedExceptions = ArithmeticException.class)
	public void divide_byZero_throwsException() throws ArithmeticException{
	Math.divide(1, 0);
	}
}
คุณสามารถมีข้อยกเว้นได้มากกว่าหนึ่งรายการexpectedExceptionsเพียงใช้วงเล็บและเครื่องหมายจุลภาคเพื่อแสดงรายการคลาสข้อยกเว้นของคุณ เช่น:

expectedException = {FirstException.class, SecondException.class, … }

ฉันจะรันการทดสอบ JUnit ใน Java ได้อย่างไร

วิธีเพิ่ม JUnit ไปที่ IntelliJ: https://stackoverflow.com/questions/19330832/setting-up-junit-with-intellij-idea คุณสามารถรันโปรเจกต์ของคุณได้ตามปกติในการทดสอบ การเรียกใช้การทดสอบทั้งหมดในคลาสทดสอบจะดำเนินการตามลำดับตัวอักษร ใน JUnit 5 คุณสามารถเพิ่มลำดับความสำคัญให้กับการทดสอบได้โดยการเพิ่ม@Orderแท็ก ตัวอย่าง:

@TestMethodOrder(OrderAnnotation.class)
public class Tests {
…
@Test
@Order(2)
public void a_test() { … }

@Test
@Order (1)
public void b_test() { … }
…
}
แม้ว่าa_test()มาก่อนb_test()ตามตัวอักษรและในโค้ดb_test()จะรันก่อนa_test()ที่นี่ เพราะ 1 มาก่อน 2 ตามลำดับ นั่นคือทั้งหมดสำหรับพื้นฐานของ JUnit ตอนนี้ เรามาจัดการกับคำถามสัมภาษณ์ JUnit ทั่วไปสองสามข้อ และเรียนรู้เพิ่มเติมเกี่ยวกับ JUnit ไปพร้อมกัน!

คำถามสัมภาษณ์ JUnit (ข้อมูลเพิ่มเติม)

ฉันได้รวบรวมคำถามสัมภาษณ์ JUnit ยอดนิยมไว้ที่นี่แล้ว หากคุณมีสิ่งที่จะเพิ่ม - อย่าลังเลที่จะทำเช่นนี้ในความคิดเห็นด้านล่าง ถาม: คุณสามารถเรียกใช้วิธีการทดสอบแบบใดเพื่อให้การทดสอบไม่ผ่านโดยอัตโนมัติ A: ล้มเหลว (“คำอธิบายข้อผิดพลาดที่นี่!”); ถาม: คุณกำลังทดสอบคลาสสุนัข ในการทดสอบวัตถุ Dog คุณต้องสร้างอินสแตนซ์ก่อนจึงจะสามารถทำการทดสอบได้ คุณจึงเขียนฟังก์ชัน setUp() เพื่อสร้างตัวอย่าง Dog คุณต้องการเรียกใช้ฟังก์ชันนี้เพียงครั้งเดียวในระหว่างการทดสอบทั้งหมด คุณต้องใส่อะไรเหนือลายเซ็นของฟังก์ชัน setUp() เพื่อให้ JUnit รู้วิธีเรียกใช้ setUp() ก่อนที่จะรันการทดสอบ A: @BeforeClass (@BeforeAll ใน JUnit 5) Q:ลายเซ็นฟังก์ชันของฟังก์ชัน setUp() ที่อธิบายไว้ข้างต้นต้องเป็นอย่างไร A: โมฆะคงที่สาธารณะ ฟังก์ชันใดๆ ที่มี @BeforeClass (@BeforeAll ใน JUnit 5) หรือ @AfterClass (@AfterAll ใน JUnit 5) จะต้องเป็นแบบคงที่ ถาม: คุณทดสอบคลาส Dog เสร็จแล้ว คุณเขียนฟังก์ชัน void tearDown() ซึ่งจะล้างข้อมูลและพิมพ์ข้อมูลไปยังคอนโซลหลังการทดสอบแต่ละครั้ง คุณต้องการให้ฟังก์ชันนี้ทำงานหลังจากการทดสอบทุกครั้ง คุณต้องใส่อะไรเหนือลายเซ็นของฟังก์ชัน tearDown() เพื่อให้ JUnit รู้ว่าต้องรัน tearDown() หลังจากรันการทดสอบแต่ละครั้ง A: @After (@AfterEach ใน JUnit 5)
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION