เครื่องมือที่ใช้ในการตรวจหาข้อผิดพลาดในโปรแกรมของเราเรียกว่า debugger ซึ่งจะช่วยให้เราเห็นว่าเกิดอะไรขึ้นบ้างในขณะที่ใช้งานโปรแกรม สามารถหยุดการทำงานของโปรแกรมในจุดที่ต้องการเพื่อตรวจสอบสถานะของโปรแกรม เช่น ค่าต่างๆของตัวแปร สามารถเปลี่ยนค่าของตัวแปรได้ทันทีเพื่อทดสอบ เป็นต้น ใน IntelliJ IDEA ก็มี debugger มาให้เราใช้งาน
การใช้งาน IntelliJ IDEA Debugger
เมื่อเราต้องการตรวจสอบข้อผิดพลาดในโปรแกรม เราต้องกำหนดจุดที่เราต้องการให้โปรแกรมระงับการทำงาน (java line breakpoint) ซึ่งทำได้โดยคลิกไปที่ด้านหน้าของบรรทัดที่ต้องการให้โปรแกรมหยุดการทำงาน หรือ คลิกที่บรรทัดที่ต้องการและเลือกเมนู Run > Toggle Breakpoint > Line Breakpoint
จากนั้นสั่ง run โปรแกรมแบบ debug หรือ เลือกจากไอคอน ที่มุมบนด้านขวา หรือ เลือกเมนู Run > Debug > เลือกเมธอด Main
เมื่อโปรแกรมทำงานมาถึงบรรทัดที่กำหนดเป็น line breakpoint จะหยุดการทำงาน โดยยังไม่ทำงานตามบรรทัดที่เรากำหนด ไฮไลต์ที่บรรทัดที่เราสั่งหยุด แสดงข้อมูลออบเจกต์และตัวแปรที่ท้ายบรรทัด และแสดงข้อมูลต่างๆในหน้าต่าง debug โดย ที่หน้าต่างด้านล่างซ้ายจะแสดงเมธอดที่กำลังทำงานโดยถ้ามีหลายเมธอดที่ถูกเรียกใช้งานต่อๆกันไปจะแสดงในหน้าต่างในลำดับแบบ stack คือเมธอดที่ถูกเรียกล่าสุดจะอยู่บนสุด และที่หน้าต่างด้านล่างขวาจะแสดงค่าของออบเจกต์และตัวแปรที่เกี่ยวข้องกับเมธอดที่เลือกในหน้าต่างด้านซ้าย โดยค่าของออบเจกต์และตัวแปรจะเป็นค่าก่อนที่จะทำงานตามคำสั่งในบรรทัดที่เราสั่งหยุดไว้
เราสามารถควบคุมการทำงานของโปรแกรมในโหมด debug โดยใช้เครื่องมือที่มีมาให้ เราสามารถเรียกใช้เครื่องมือเหล่านี้จากเมนู Run > Debugging Actions ได้เช่นกัน
Show Execution Point : แสดงบรรทัดที่เรากำหนด line breakpopint ไว้ในหน้าต่างเขียนโปรแกรม
Step Over : ทำคำสั่งในบรรทัด line breakpoint แล้วเลื่อนไปหยุดในบรรทัดคำสั่งถัดไป
Step Into : แสดงบรรทัดคำสั่งของเมธอดที่ถูกเรียกใช้ในบรรทัด line breakpoint
Force Step Into : แสดงบรรทัดคำสั่งของเมธอด Java API ที่ถูกเรียกใช้ในบรรทัด line brakepoint ซึ่งโดยปรกติหน้าต่าง debug จะข้ามไป
Step Out : ทำคำสั่งที่เหลือในเมธอดที่ Step Into และกลับมาหยุดที่บรรทัด line breakpoint
Run to Cursor : ทำคำสั่งไปจนถึงบรรทัดก่อนหน้าบรรทัดที่เราคลิกเลือกไว้
Rerun : ทำคำสั่งใหม่แต่ต้น
Resume Program : ทำคำสั่งต่อไปจนจบโปรแกรม และหากต้องการจบการ debug ควรใช้ resume program แทนที่จะ stop กลางคัน
ในกรณีที่เรามีการกำหนด line breakpoint ไว้หลายที่หรือหลายไฟล์ เราสามารถตรวจสอบได้โดยไปที่เมนู Run > View Breakpoints…
หรือคลิกที่รูป ที่แถบเครื่องมือด้านซ้ายของหน้าต่าง debug
การตรวจหาข้อผิดพลาดส่วนใหญ่คือการตรวจสอบค่าของตัวแปรต่างๆว่าเป็นไปตามที่เราออกแบบไว้หรือไม่ โดยเราอาจจะใช้ Step Over เพื่อให้โปรแกรมทำงานทีละบรรทัดและตรวจสอบค่าของตัวแปรที่เปลี่ยนแปลงไป โดยดูค่าของตัวแปรต่างๆที่เกี่ยวข้องกับรรทัดที่เราหยุดไว้ ได้ทางหน้าต่างด้านซ้าย ซึ่งจะมีสัญญลักณ์บอกไว้ เช่น p คือ พารามิเตอร์ f คือ ฟิลด์ เป็นต้น หรือจะเอาเมาส์ไปชี้เพื่อดูคำอธิบายก็ได้
สำหรับสัญญลักณ์ที่เป็นรูปแว่นตาคือรายการตัวแปรที่เราต้องการติดตามการเปลี่ยนแปลง เรียกว่า watch เป็นรายการของตัวแปรที่เราสนใจติดตามเพื่อให้ดูได้ง่ายๆไม่ต้องไปคอยขยายโครงสร้างด้านบนเพื่อดูค่า IntelliJ IDEA จะดึงค่าของตัวแปรที่สำคัญมาให้โดยอัตโนมัติ หากเราต้องการเพิ่มตัวแปรใน watch สามารถทำได้โดย ระบุชื่อตัวแปรที่ต้องการที่ช่องด้านบนของหน้าต่างขวา แล้วเลือก + ที่ท้ายช่อง หรือ กด Ctrl+Shift+Enter เพื่อเพิ่มตัวแปรใน watch หากต้องการยกเลิกให้คลิกที่ตัวแปรและกดปุ่ม Delete หรือคลิกขวาที่ตัวแปรและเลือก Remove Watch
นอกจาก Line Breakpoint แล้วยังมี Field Watchpoint ซึ่งเป็นการกำหนดการหยุดเมื่อฟิลด์ที่เราสนใจถูกเปลี่ยนแปลงหรือถูกใช้งานในโปรแกรม และจะแสดงบรรทัดที่ทำให้เกิดการเปลี่ยนแปลงหรือถูกใช้งาน เรากำหนด Field Watchpoint โดยคลิกที่หน้าบรรทัดประกาศฟิลด์ในคลาสที่ต้องการ และเลือก Field Watchpoint ซึ่งจะแสดงเครื่องหมาย หน้าบรรทัดฟิลด์ที่เลือก
ค่าตั้งต้นของ File Watchpoint คือหยุดเมื่อฟิลด์ถูกเปลี่ยนแปลง เราสามารถกำหนดการหยุดเมื่อฟิลด์ถูกใช้งานได้โดย คลิกขวาที่ และเลือก Field access
และคลิกที่ More เลือก Remove once hit ถ้าต้องการให้หยุดเพียงครั้งเดียว
ในกรณีที่เรากำหนด Line Breakpoint ที่บรรทัดที่มีการเรียกใช้เมธอดซ้อนเมธอดหลายชั้น หากเราใช้ Step Into มันจะพาเราเข้าไปในทุกๆเมธอดซึ่งเราอาจจะสนใจติดตามผลในเฉพาะบางเมธอด เราสามารถใช้เครื่องมือ Smart Step Into เพื่อเลือกดูรายละเอียดเฉพาะเมธอดที่ต้องการได้ โดยเมื่อโปรแกรมมาหยุดที่บรรทัดที่กำหนด ให้เรียกจากเมนู Run > Debugging Actions > Smart Step Into ซึ่งจะแสดงเมธอดมาให้เราเลือก
เมื่อเราต้องการแก้ไขค่าของตัวแปรในขณะที่กำลัง debug เราสามารถทำได้โดยคลิกขวาที่ตัวแปรที่ต้องการและเลือก Set Value…
Unit Testing
การทำ unit testing เป็นด่านแรกของการทดสอบโปรแกรมทำโดยผู้พัฒนาโปรแกรม โดยมากจะหมายถึงการทดสอบเมธอดเพราะส่วนใหญ่จะเป็นการเขียนโปรแกรมเพื่อเป็นส่วนหนึ่งของแอพพลิเคชั่นขนาดใหญ่ การทดสอบอาจจะมีการแก้ไขและทดสอบใหม่ซ้ำหลายๆรอบ ดังนั้นการใช้เครื่องมือช่วยในการทดสอบจะช่วยประหยัดเวลาๆได้มา ซึ่งในที่นี้เราจะใช้ JUnit ซึ่งเป็น Java API ที่เป็นIเฟรมเวิร์คที่ช่วยช่วยให้เราทดสอบได้อย่างอัตโนมัติมากขึ้น
เราเรียกใช้งาน JUnit โดยไปที่ไฟล์คลาสที่ต้องการทำสอบ คลิกที่ชื่อคลาสให้เป็นไฮไลท์ กดปุ่ม Alt+Enter และเลือก Creat Test
โปรแกรมจะสอบถามไดเร็คทอรี่ที่ต้องการให้บันทึกไฟล์สำหรับทดสอบ ให้เลือก OK
จากนั้นโปรแกรมจะแสดงหน้าต่างให้เราเลือกเครื่องมือในการทดสอบ โดยในช่อง Testing Library ให้เลือก JUnit เวอร์ชั่นล่าสุด (แต่ในกรณีที่โปรแกรมถูกพัฒนาขึ้นมาและผ่านการทดสอบด้วย JUnit เวอร์ชั่นที่เก่ากว่า ให้เลือกเวอร์ชั่นนั้นเพราะอาจจะทดสอบด้วย JUnit เวอร์ชั่นล่าสุดไม่ผ่าน) และเลือกทุกเมธอดที่ต้องการทดสอบจากกรอบด้านล่าง
JUnit จะสร้างไฟล์เพื่อการทดสอบขึ้นมาในชื่อเดียวกับคลาสและตามด้วยข้อความ Test
จะเห็นว่าข้อความ junit เป็นสีแดงเนื่องจากเรายังไม่ได้เพิ่มไลบรารี่ของ JUnit เข้ามาในโครงการของเรา ให้คลิกที่ข้อความ junit ที่เป็นสีแดง แล้วกด Alt + Enter และเลือก Add ‘JUnit xxxxx’ to classpath โดยเลือกเวอร์ชั่นตามที่เราเลือกในขั้นตอนสร้างไฟล์ทดสอบ
โปรแกรมให้เลือกว่าใช้ไลบรารี่จากแหล่งใด เลือกแหล่งที่มาที่เป็น IntelliJ IDEA (ถ้ามี) หรือแหล่งอื่นตามเวอร์ชั่นที่เราจะใช้
จะเห็นว่าสีแดงตรงคำอธิบาย (annotation) จะหายไป และถ้าเราไปดูที่ External Libraries ในหน้าต่างโครงการจะเห็นว่ามีไลบรารี่ของ JUnit เพิ่มเข้ามา คำอธิบาย (annotation) นี้เป็นตัวบอก JUnit ว่าเราจะทดสอบเมธอดใดบ้าง เมธอดใดที่ไม่มีคำอธิบายกำกับจะไม่ถูกทดสอบ คำอธิบายนี้จะมีโดเมนแตกต่างกันไปตามแต่ไลบรารี่ของ JUnit ที่เราเลือกใช้แต่คำสั่งด้านท้ายจะเหมือนกัน
จากนั้นเราจะสร้าง run configuration ของการทดสอบเพื่อใช้ในการ run ทดสอบโปรแกรมโดยคลิกขวาที่บรรทัดสุดท้ายของคลาส (คลิกอย่าให้โดนขอบเขตของเมธอด) แล้วเลือก Modify Run Configuration…
ที่หน้าต่าง Create Run Configuration เลือก Apply และเลือก OK จะได้ run configuration ของการทดสอบแสดงที่มุมบนขวาเพิ่มขึ้นมาจากเดิมที่มีแค่ Main
ในการทดสอบเราจะต้องกำหนดเงื่อนไขในการทดสอบในบล๊อกของเมธอด หากเราไม่กำหนดและเริ่มการทดสอบ ระบบจะแจ้งว่าทดสอบผ่าน (เพราะว่าไม่ได้ทดสอบอะไร) ดังนั้นเราจะเพิ่มบรรทัดคำสั่ง fail (“Please implemnet test.”) ในแต่ละเมธอดเพื่อเตือนว่าเรายังไม่ได้กำหนดเงื่อนไขการทดสอบสำหรับแต่ละเมธอด
ในการ run เพื่อทดสอบเราจะเลือก run configuration ของคลาสทดสอบที่กำหนดไว้ จากมุมบนขวา
เมื่อเรา run ทดสอบแล้ว ที่ด้านล่างจะแสดงหน้าต่างของผลการทดสอบ โดยหน้าต่างด้านซ้ายจะแสดงคลาสและเมธอดที่เราทำการทดสอบ ซึ่งเราสามารถคลิกเพื่อเลือกแสดงรายละเอียดเฉพาะผลการทดสอบของคลาสหรือเมธอดได้
ส่วนหน้าต่างด้านขวาจะเป็นรายละเอียดผลการทดสอบ และด้านบนของหน้าต่างด้านขวาจะเป็นผลการทดสอบ ซึ่งในที่นี้คือ Tests failed: 2 เพราะเราใช้คำสั่ง fail() บังคับให้เกิดข้อผิดพลาด
การกำหนดเงื่อนในการทดสอบในแต่ละเมธอดจะต้องจบเบ็ดเสร็จในเมธอดนั้นๆและไม่สามารถอ้างอิงผลลัพธ์หรือตัวแปรจากเมธอดอื่น จากตัวอย่างด้านล่างเมธอดเราต้องสร้างออบเจกต์ขึ้นมาเพื่อการใช้งาน ดังนั้นการเขียนเงื่อนไขในแต่ละเมธอดก็จะต้องกำหนดการสร้างออบเจกต์ของใครของมัน ในการตรวจสอบผลเราใช้เมธอด assertEquals(ค่าที่คาดหวัง, ผลลัพธ์จากเมธอด)
เมื่อ run โปรแกรม ทางกรอบด้านซ้ายจะแสดงเครื่องหมายถูกหน้าแต่ละเมธอดว่าการทดสอบผ่าน และจะเห็นผลการทดสอบที่ด้านบนของหน้าต่างขวา
ตัวอย่างด้านล่างคือในกรณีที่มีบางเมธอดทดสอบไม่ผ่าน
เมธอด assert… คือการยืนยันผลการทดสอบโดยเปรียบเทียบผลลัพธ์ที่ได้กับค่าที่คาดไว้ เมธอด assert… จะมีหลายแบบ เช่น
assertEquals() ใช้เพื่อเปรียบเทียบว่าผลลัพธ์ตรงกับค่าที่กำหนดไว้ ถ้าเราใช้เปรียบเทียบออบเจกต์จะหมายถึงออบเกจต์ทั้งสองจะต้องเป็นออบเจกต์เดียวกัน assertEquals() เปรียบเทียบออบเจกต์โดยใช้เมธอด equals()
assertNoEquals() ใช้เพื่อเปรียบเทียบว่าผลลัพธ์ไม่ตรงกับค่าที่กำหนดไว้
assertArrayEquals() ใช้เพื่อเปรียบเทียบผลลัพธ์กับค่าที่กำหนดไว้ในรูปแบบอาเรย์ โดยอาเรย์จะเท่ากันก็ต่อเมื่อมีขนาดเท่ากันและแต่ละสมาชิกในอาเรย์ก็ต้องเท่ากันและเรียงลำดับเหมือนกันด้วย
assertNull() และ assertNotNull() ใช้เพื่อเปรียบเทียบว่าผลลัพธ์เป็น null หรือ not null
assertSame() และ assertNotSame() ใช้เพื่อเปรียบเทียบผลลัพธ์กับค่าที่กำหนดที่เป็นออบเจกต์โดยจะเปรียบเทียบค่าอ้างอิงของเจกต์
assertThat() ใช้เพื่อเปรียบเทียบผลลัพธ์กับช่วงของค่าที่กำหนด การเปรียบเทียบทำโดยใช้ matcher ของ JUnit API โดยจะมีมาใน JUnit เวอร์ชั่น 4.4 เป็นต้นไป
นอกจากคำอธิบาย @org.junit.jupiter.api.Test แล้วยังมีคำอธิบายอื่นๆที่ช่วยให้เราทำงานได้สะดวกขึ้น เช่น ในกรณีที่เราต้องจัดเตรียมสิ่งแวดล้อมเพื่อการทดสอบอย่างการสร้างออบเจกต์ การกำหนดค่าให้ตัวแปรจำนวนมาก ถ้าต้องทำในทุกเมธอดอาจจะเกิดผิดพลาดได้ ดังนั้นเราสามารถดึงเอาส่วนที่ทำซ้ำๆเหล่านั้นมากำหนดในที่เดียวและให้ทุกเมธอดเรียกใช้ก่อนการทดสอบได้โดยกำกับด้วยคำอธิบาย @org.junit.jupiter.api.BeforeEach จากตัวอย่างด้านล่างเป็นการดึงเอาการสร้างออบเจกต์ออกมาไว้ที่เมธอด setup() อย่างไรก็ตามการใช้งานยังคงต้องคำนึงถึงโครงสร้างของภาษาจาวา เช่น เราต้องสร้างฟิลด์ในระดับคลาส ส่วนการสร้างออบเจกต์และการกำหนดค่าสามารถอยู่ในเมธอด setup() ได้
จากตัวอย่างด้านล่างเราดึงเอาการสร้างออบเจกต์ NumberArrayTest จากเดิมที่อยู่ในแต่ละเมธอดที่ต้องการทดสอบมาสร้างฟิลด์ในระดับคลาส แล้วสร้างเมธอด setup() เพื่อสร้างออบเจกต์ NumberArrayTest และกำกับเมธอด setup() ด้วย @org.junit.jupiter.api.BeforeEach ซึ่งหมายถึงให้เมธอดนี้ทำงานก่อนการทดสอบของทุกๆเมธอดที่ต้องการทดสอบ
ในกรณีที่เราต้องการทดสอบด้วยค่าหลายๆค่า แทนที่จะต้องสร้างเมธอดซ้ำๆกันหลายอัน เราสามารถใช้คำอธิบาย @ParameterizedTest เพื่อบอกว่าเมธอดจะถูกทดสอบด้วยค่าที่ส่งให้ และกำหนดค่าและผลลัพธ์ที่ต้องการด้วย @CsvSource จากตัวอย่างด้านล่าง เรากำหนดชุดของค่าตั้งต้นและผลลัพธ์ที่ต้องการ และกำหนดพารามิเตอร์ที่เมธอดเพื่อรับค่าไปใช้
ซึ่งผลลัพธ์ที่ได้จะแสดงรายละเอียดของชุดข้อมูลที่เราใช้ด้วย
รายละเอียดการใช้งานอื่นๆของ JUnit เวอร์ชั่น 5สามารถดูได้จาก https://junit.org/junit5/docs/current/api/