การทดสอบหน่วยไม่ควรใช้วิธีการของฉันเอง?


83

วันนี้ฉันกำลังดูวิดีโอ "พื้นฐานJUnit " และผู้เขียนกล่าวว่าเมื่อทำการทดสอบวิธีการที่กำหนดในโปรแกรมของคุณคุณไม่ควรใช้วิธีการอื่นของคุณเองในกระบวนการ

เพื่อที่จะเจาะจงมากขึ้นเขากำลังพูดถึงการทดสอบวิธีการสร้างเรกคอร์ดที่ใช้ชื่อและนามสกุลของอาร์กิวเมนต์และใช้เพื่อสร้างเรกคอร์ดในตารางที่กำหนด แต่เขาอ้างว่าในกระบวนการทดสอบวิธีนี้เขาไม่ควรใช้วิธีDAOอื่น ๆ ของเขาในการสืบค้นฐานข้อมูลเพื่อตรวจสอบผลลัพธ์สุดท้าย (เพื่อตรวจสอบว่าบันทึกถูกสร้างขึ้นจริงด้วยข้อมูลที่ถูกต้อง) เขาอ้างว่าสิ่งนั้นเขาควรเขียนรหัสJDBCเพิ่มเติมเพื่อสืบค้นฐานข้อมูลและตรวจสอบผลลัพธ์

ฉันคิดว่าฉันเข้าใจจิตวิญญาณของการอ้างสิทธิ์ของเขา: คุณไม่ต้องการให้กรณีทดสอบของวิธีใดวิธีหนึ่งขึ้นอยู่กับความถูกต้องของวิธีอื่น ๆ (ในกรณีนี้วิธี DAO) และสิ่งนี้สามารถทำได้โดยการเขียนการตรวจสอบของคุณเองอีกครั้ง / สนับสนุนรหัส (ซึ่งควรจะเฉพาะเจาะจงมากขึ้นและเพ่งความสนใจไปจึงรหัสที่ง่ายขึ้น)

อย่างไรก็ตามเสียงในหัวของฉันเริ่มคัดค้านด้วยข้อโต้แย้งเช่นการทำสำเนารหัสความพยายามเพิ่มเติมที่ไม่จำเป็น ฯลฯ ฉันหมายถึงถ้าเราใช้แบตเตอรี่ทดสอบทั้งหมดและเราทดสอบวิธีสาธารณะทั้งหมดของเราอย่างละเอียด (รวมถึงวิธี DAO ในกรณีนี้) ไม่ควร ไม่เป็นไรที่จะใช้วิธีการเหล่านั้นบางอย่างขณะทดสอบวิธีอื่น ๆ หากหนึ่งในนั้นไม่ได้ทำในสิ่งที่มันควรจะเป็นกรณีทดสอบของตัวเองจะล้มเหลวและเราสามารถแก้ไขและเรียกใช้แบตเตอรี่ทดสอบอีกครั้ง ไม่จำเป็นต้องทำสำเนารหัส (แม้ว่ารหัสที่ซ้ำกันจะค่อนข้างง่ายกว่า) หรือสูญเสียความพยายาม

ฉันมีความรู้สึกที่แข็งแกร่งเกี่ยวกับเรื่องนี้เนื่องจากแอปพลิเคชันExcel - VBA หลายตัวที่ฉันเขียน (ต้องผ่านการทดสอบหน่วยอย่างถูกต้องด้วยRubberduck สำหรับ VBA ) ซึ่งการใช้คำแนะนำนี้จะหมายถึงการทำงานพิเศษเพิ่มเติมจำนวนมากโดยไม่มีประโยชน์

คุณช่วยแบ่งปันความเข้าใจเกี่ยวกับเรื่องนี้ได้ไหม?


79
เป็นเรื่องแปลกที่จะเห็นการทดสอบหน่วยที่เกี่ยวข้องกับฐานข้อมูลเลย
Richard Tingle

4
IMO เป็นเรื่องดีที่จะเรียกคลาส IFF อื่น ๆ ว่าเร็วพอ เยาะเย้ยสิ่งที่ต้องไปยังดิสก์หรือผ่านเครือข่าย ไม่มีเหตุผลในการล้อเลียน IMO ระดับ ol 'ธรรมดา
RubberDuck

2
มีลิงค์ไปยังวิดีโอนี้หรือไม่?
candied_orange

17
" ต้องผ่านการทดสอบหน่วยอย่างถูกต้องด้วย Rubberduck สำหรับ VBA " - ท่านที่รักเพิ่งทำวันของฉัน ฉันจะแก้ไขคำผิดและแก้ไขจาก "RubberDuck" เป็น "Rubberduck" แต่ฉันรู้สึกเหมือนนักสแปมบางคนทำเช่นนั้นและเพิ่มลิงก์ไปยังrubberduckvba.com (ฉันเป็นเจ้าของชื่อโดเมนและร่วมเป็นเจ้าของโครงการด้วย @RubberDuck) - ดังนั้นฉันจะแสดงความคิดเห็นที่นี่แทน อย่างไรก็ตามมันยอดเยี่ยมมากที่ได้เห็นคนใช้เครื่องมือที่รับผิดชอบคืนนอนไม่หลับส่วนใหญ่ของฉันในช่วงสองปีที่ผ่านมา! =)
Mathieu Guindon

4
@ Mat'sMug และ RubberDuck ฉันรักในสิ่งที่คุณทำกับ Rubberduck โปรดติดตามต่อไป แน่นอนว่าชีวิตของฉันง่ายขึ้นเพราะมัน (ฉันได้ทำโปรแกรมเล็ก ๆ และต้นแบบใน Excel VBA) BTW, Rubberduck พูดถึงในโพสต์ของฉันเป็นเพียงฉันพยายามที่จะทำดีกับ RubberDuck ตัวเองซึ่งเป็นสิ่งที่ดีสำหรับฉันที่นี่ใน PE :)
carlossierra

คำตอบ:


186

วิญญาณของการเรียกร้องของเขาถูกต้องแน่นอน จุดทดสอบหน่วยคือการแยกรหัสทดสอบโดยไม่ขึ้นต่อกันเพื่อให้สามารถจดจำพฤติกรรมที่ผิดพลาดได้อย่างรวดเร็วเมื่อเกิดขึ้น

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

ท้ายที่สุดคุณไม่ได้ให้คะแนนบนมันสิ่งสำคัญคือผลิตภัณฑ์สุดท้ายของซอฟต์แวร์ที่กำลังทดสอบ แต่คุณจะต้องคำนึงถึงเมื่อคุณก้มกฏและตัดสินใจเมื่อการแลกเปลี่ยนมีค่า


117
ไชโยสำหรับลัทธิปฏิบัตินิยม
Robert Harvey

6
ฉันจะเพิ่มที่ไม่ได้มีความน่าเชื่อถือมากเท่าความเร็วที่เป็นปัญหาที่นำไปสู่การขัดออกพึ่งพา การทดสอบในมนต์แยกนั้นเป็นเรื่องที่น่าสนใจมาก
Michael Durrant

4
"... การทดสอบหน่วยเป็นเครื่องมือและมันมีไว้เพื่อให้บริการตามวัตถุประสงค์ของคุณมันไม่ใช่แท่นบูชาที่จะต้องอธิษฐาน" - นี่!
Wayne Conrad

15
"ไม่ใช่แท่นบูชาที่ต้องสวดอ้อนวอน" - โค้ช TDD โกรธเข้ามา!
Den

5
@ jpmc26: ยิ่งแย่ไปกว่านั้นคุณไม่ต้องการให้หน่วยทดสอบทั้งหมดของคุณผ่านเพราะพวกเขาจัดการบริการเว็บอย่างถูกต้องซึ่งเป็นพฤติกรรมที่ถูกต้องและถูกต้อง แต่ไม่เคยใช้รหัสในตอนที่มันเป็นจริง! เมื่อคุณเริ่มต้นมันคุณจะต้องเลือกว่าจะ "ขึ้น" หรือ "ลง" และทดสอบทั้งสองอย่าง
Steve Jessop

36

ฉันคิดว่านี่เป็นศัพท์เฉพาะ สำหรับหลาย ๆ คน "การทดสอบหน่วย" เป็นสิ่งที่เฉพาะเจาะจงมากและโดยการกำหนดไม่สามารถมีเงื่อนไขการผ่าน / ล้มเหลวที่ขึ้นอยู่กับรหัสใด ๆ ที่อยู่นอกหน่วย (วิธีการฟังก์ชั่น ฯลฯ ) ที่ถูกทดสอบ ซึ่งจะรวมถึงการมีปฏิสัมพันธ์กับฐานข้อมูล

สำหรับผู้อื่นคำว่า "การทดสอบหน่วย" นั้นหลวมกว่าและครอบคลุมการทดสอบอัตโนมัติทุกประเภทรวมถึงรหัสทดสอบที่ทดสอบส่วนต่าง ๆ ของแอปพลิเคชัน

ผู้พิถีพิถันในการทดสอบ (ถ้าฉันอาจใช้คำนั้น) อาจเรียกว่าการทดสอบการรวมซึ่งแตกต่างจากการทดสอบหน่วยบนพื้นฐานที่การทดสอบขึ้นอยู่กับรหัสบริสุทธิ์มากกว่าหนึ่งหน่วย

ฉันสงสัยว่าคุณกำลังใช้เวอร์ชันหลวมของคำว่า "การทดสอบหน่วย" และจริง ๆ แล้วหมายถึง "การทดสอบการรวม"

การใช้คำจำกัดความเหล่านั้นคุณไม่ควรพึ่งพาโค้ดนอกหน่วยในการทดสอบใน "การทดสอบหน่วย" อย่างไรก็ตามใน "การทดสอบการรวม" มันมีเหตุผลอย่างสมบูรณ์แบบที่จะเขียนการทดสอบที่ออกกำลังกายบางส่วนของรหัสแล้วตรวจสอบฐานข้อมูลสำหรับเกณฑ์การผ่าน

ไม่ว่าคุณจะขึ้นอยู่กับการทดสอบการรวมหรือการทดสอบหน่วยหรือทั้งสองอย่างเป็นหัวข้อการอภิปรายที่ใหญ่กว่ามาก


คุณคิดถูกแล้ว ฉันใช้ข้อกำหนดในทางที่ผิดและฉันสามารถดูได้ในขณะนี้ว่าการทดสอบแต่ละประเภทสามารถจัดการได้อย่างเหมาะสมมากขึ้นอย่างไร ขอบคุณ!
carlossierra

11
"สำหรับคนอื่น ๆ คำว่า" การทดสอบหน่วย "เป็นเรื่องที่ยุ่งยากมาก - นอกเหนือจากสิ่งอื่น ๆ ที่เรียกว่า" กรอบการทดสอบหน่วย "เป็นวิธีที่สะดวกในการจัดระเบียบและเรียกใช้การทดสอบระดับสูงกว่า ;-)
Steve Jessop

1
แทนที่จะแยกความแตกต่างระหว่าง "บริสุทธิ์" กับ "หลวม" ซึ่งเต็มไปด้วยความหมายแฝงฉันขอแนะนำความแตกต่างระหว่างผู้ที่ดู "หน่วย" "การพึ่งพา" ฯลฯ จากมุมมองของโดเมน (เช่น "การพิสูจน์ตัวตน") เป็นหน่วย "ฐานข้อมูล" จะนับเป็นการพึ่งพาและอื่น ๆ ) เทียบกับผู้ที่ดูพวกเขาจากมุมมองภาษาการเขียนโปรแกรม (เช่นวิธีการคือหน่วยหน่วยชั้นเรียนมีการอ้างอิง) ฉันพบว่าการกำหนดสิ่งที่มีความหมายโดยวลีเช่น "หน่วยที่อยู่ระหว่างการทดสอบ" สามารถเปลี่ยนข้อโต้แย้ง ("คุณผิด!") เป็นการอภิปราย ("นี่เป็นระดับที่เหมาะสมของความละเอียดหรือไม่")
Warbo

1
@EricKing แน่นอนสำหรับคนส่วนใหญ่ในประเภทแรกของคุณความคิดที่เกี่ยวข้องกับฐานข้อมูลทั้งหมดในการทดสอบหน่วยคือคำสาปแช่งไม่ว่าคุณจะใช้ DAO ของคุณหรือกดฐานข้อมูลโดยตรง
Periata Breatta

1
@AmaniKilumanga คำถามคือโดยทั่วไป "มีคนบอกว่าฉันไม่ควรทำสิ่งนี้ในการทดสอบหน่วย แต่ฉันทำมันในการทดสอบหน่วยและฉันคิดว่ามันใช้ได้โอเคไหม? และคำตอบของฉันคือ "ใช่ แต่คนส่วนใหญ่จะเรียกว่าการทดสอบการรวมแทนการทดสอบหน่วย" เท่าที่คำถามของคุณฉันไม่ทราบว่าคุณหมายถึงโดย "การทดสอบแบบบูรณาการอาจใช้วิธีการผลิตรหัส?"
Eric King

7

คำตอบคือใช่และไม่ใช่ ...

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

การทดสอบข้ามหน่วยเหล่านี้ดีสำหรับการครอบคลุมโค้ดและเมื่อการทดสอบการใช้งานแบบ end-to-end แต่มันมาพร้อมกับข้อบกพร่องเล็กน้อยที่คุณควรทราบ:

  • หากไม่มีการทดสอบแยกเพื่อยืนยันสิ่งที่เกิดขึ้นการทดสอบ "หน่วยข้าม" ที่ล้มเหลวจะต้องมีการแก้ไขปัญหาเพิ่มเติมเพื่อตรวจสอบว่ามีอะไรผิดปกติกับรหัสของคุณ
  • การใช้การทดสอบข้ามหน่วยมากเกินไปจะช่วยให้คุณหลุดพ้นจากความคิดในการทำสัญญาที่คุณควรจะทำเมื่อเขียนโค้ดที่เน้นวัตถุเชิงวัตถุ การทดสอบแบบแยกมักจะสมเหตุสมผลเนื่องจากหน่วยของคุณควรทำการทดสอบพื้นฐานเพียงครั้งเดียว
  • การทดสอบตั้งแต่ต้นจนจบเป็นสิ่งที่ต้องการ แต่อาจเป็นอันตรายได้หากการทดสอบต้องการให้คุณเขียนไปยังฐานข้อมูลหรือดำเนินการบางอย่างที่คุณไม่ต้องการให้เกิดขึ้นในสภาพแวดล้อมการใช้งานจริง หนึ่งในหลาย ๆ เหตุผลที่กรอบการเยาะเย้ยเช่นMockitoนั้นเป็นที่นิยมมากเพราะมันช่วยให้คุณสามารถปลอมวัตถุและเลียนแบบการจบแบบสิ้นสุดโดยไม่ต้องเปลี่ยนอะไรที่คุณไม่ควรทำ

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

ความจริงที่น่าเศร้า แต่จริงคือถ้าคุณใช้การทดสอบอัตโนมัติเลยคุณมีนักพัฒนาจำนวนมากอยู่แล้ว การทดสอบที่ดีเป็นสิ่งแรกที่จะเลื่อนเมื่อทีมพัฒนาต้องเผชิญกับกำหนดเวลาที่ยากลำบาก ดังนั้นตราบใดที่คุณยังคงยึดติดกับปืนของคุณและเขียนการทดสอบยูนิตที่มีความหมายว่ารหัสของคุณควรทำงานอย่างไรฉันก็จะไม่หมกมุ่นกับการทดสอบของคุณที่ "บริสุทธิ์"


4

ปัญหาหนึ่งในการใช้วิธีการวัตถุอื่นเพื่อทดสอบเงื่อนไขคือคุณจะพลาดข้อผิดพลาดที่ยกเลิกกัน ที่สำคัญกว่านั้นคือคุณพลาดความเจ็บปวดจากการใช้งานการทดสอบที่ยากลำบากและความเจ็บปวดนั้นกำลังสอนคุณเกี่ยวกับโค้ดพื้นฐานของคุณ การทดสอบที่เจ็บปวดตอนนี้หมายถึงการบำรุงรักษาที่เจ็บปวดในภายหลัง

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


1
รหัสที่นี่อยู่ในหน่วย หากคุณจำเป็นต้องโทรไปยังโพรซีเดอร์และกระบวนการอื่น ๆ ซึ่งไม่ใช่ยูนิตสำหรับฉัน หากมีการโทรหลายครั้งและต้องมีการตรวจสอบหลายขั้นตอนหากเป็นรหัสชิ้นใหญ่กว่าหน่วยให้แยกเป็นชิ้นเล็ก ๆ ซึ่งครอบคลุมตรรกะชิ้นเดียว โดยปกติแล้วการเรียก DB จะถูกเยาะเย้ยหรือจะใช้ในหน่วยความจำ DB ในการทดสอบหน่วยผ่านการเข้าถึง DB จริงและต้องการยกเลิกข้อมูลหลังจากการทดสอบ
dlb

1

หากหน่วยของฟังก์ชันการทำงานที่ทดสอบคือ 'เป็นข้อมูลที่จัดเก็บอย่างต่อเนื่องและเรียกคืนได้' จากนั้นฉันจะมีการทดสอบการทดสอบหน่วยที่ - เก็บไว้ในฐานข้อมูลจริงทำลายวัตถุใด ๆ ที่ถือการอ้างอิงไปยังฐานข้อมูล วัตถุกลับ

การทดสอบว่าเร็กคอร์ดถูกสร้างขึ้นในฐานข้อมูลดูเหมือนว่าจะเกี่ยวข้องกับรายละเอียดการใช้งานของกลไกการจัดเก็บมากกว่าการทดสอบหน่วยการทำงานที่เปิดเผยกับส่วนที่เหลือของระบบ

คุณอาจต้องการทางลัดการทดสอบเพื่อปรับปรุงประสิทธิภาพการทำงานโดยใช้ฐานข้อมูลจำลอง แต่ฉันมีผู้รับเหมาที่ทำอย่างนั้นและทิ้งไว้เมื่อสิ้นสุดสัญญากับระบบที่ผ่านการทดสอบดังกล่าว แต่จริง ๆ แล้วไม่ได้เก็บอะไรไว้ในฐานข้อมูล ระหว่างการรีบูตระบบ

คุณสามารถยืนยันได้ว่า 'หน่วย' ในการทดสอบหน่วยแสดงถึง 'หน่วยของรหัส' หรือ 'หน่วยของฟังก์ชั่น' ฟังก์ชั่นที่สร้างขึ้นโดยหน่วยรหัสจำนวนมากในคอนเสิร์ต ฉันไม่พบความแตกต่างที่มีประโยชน์ - คำถามที่ฉันควรทราบคือ 'การทดสอบบอกคุณบางอย่างเกี่ยวกับว่าระบบให้คุณค่าทางธุรกิจ' หรือไม่และ 'เป็นการทดสอบที่เปราะบางหากการดำเนินการเปลี่ยนแปลงหรือไม่' การทดสอบอย่างที่อธิบายไว้นั้นมีประโยชน์เมื่อคุณกำลังใช้ระบบ TDDing - คุณยังไม่ได้เขียน 'รับวัตถุจากบันทึกฐานข้อมูล' ดังนั้นจึงไม่สามารถทดสอบฟังก์ชันการทำงานเต็มรูปแบบได้ - แต่จะเปราะบางต่อการเปลี่ยนแปลงการใช้งาน ลบออกเมื่อการดำเนินการเต็มรูปแบบทดสอบ


1

วิญญาณนั้นถูกต้อง

ในอุดมคติแล้วในการทดสอบหน่วยคุณกำลังทดสอบหน่วย (วิธีเฉพาะหรือชั้นเรียนขนาดเล็ก)

เป็นการดีที่คุณจะเริ่มต้นระบบฐานข้อมูลทั้งหมด นั่นคือคุณจะใช้วิธีการของคุณในสภาพแวดล้อมที่ปลอมและเพียงให้แน่ใจว่ามันเรียก DB APIs ที่ถูกต้องในลำดับที่ถูกต้อง คุณอย่างชัดเจนในเชิงบวกไม่ต้องการทดสอบ DB เมื่อทดสอบหนึ่งในวิธีการของคุณเอง

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

แน่นอนว่านี่เป็นเป้าหมายที่สูงส่งในโครงการซอฟต์แวร์ใด ๆ ที่ไม่ได้ทำสิ่งนี้ตั้งแต่เริ่มต้น แต่มันคือวิญญาณของการทดสอบหน่วย

โปรดทราบว่ามีการทดสอบอื่น ๆ เช่นการทดสอบคุณสมบัติการทดสอบพฤติกรรม ฯลฯ ที่แตกต่างจากวิธีการนี้ - อย่าสับสน "การทดสอบ" กับ "การทดสอบหน่วย"


"ตรวจสอบให้แน่ใจว่าเรียก API ฐานข้อมูลที่ถูกต้องตามลำดับที่ถูกต้อง" - ใช่แน่นอน จนกระทั่งปรากฎว่าคุณอ่านเอกสารผิดและลำดับที่ถูกต้องจริงที่คุณควรใช้มีความแตกต่างอย่างสิ้นเชิง อย่าจำลองระบบนอกการควบคุมของคุณ
Joker_vD

4
@Joker_vD นั่นคือการทดสอบการรวมระบบ ในการทดสอบหน่วยระบบภายนอกควรล้อเลียนอย่างแน่นอน
Ben Aaronson

1

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

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

คำถามของคุณบ่งชี้ว่าคุณกำลังคิดตามเส้นที่ถูกต้อง ในขณะที่คุณสงสัยว่าการทดสอบหน่วยเป็นรหัสเกินไป สิ่งสำคัญคือการทำให้พวกเขาบำรุงรักษาและปรับให้เข้ากับการเปลี่ยนแปลงในอนาคตเช่นเดียวกับส่วนที่เหลือของรหัสฐานของคุณ พยายามทำให้พวกเขาอ่านและกำจัดการทำซ้ำและคุณจะมีชุดทดสอบที่ดีขึ้นกว่าเดิม และชุดทดสอบที่ดีก็คือชุดที่ใช้งาน และชุดทดสอบที่ใช้แล้วช่วยในการค้นหาข้อผิดพลาดในขณะที่ชุดที่ไม่ได้ใช้ก็ไม่มีค่า


1

บทเรียนที่ดีที่สุดที่ฉันเรียนรู้เมื่อเรียนรู้เกี่ยวกับการทดสอบหน่วยและการทดสอบบูรณาการคือไม่ใช้วิธีทดสอบ แต่เป็นพฤติกรรมการทดสอบ กล่าวอีกนัยหนึ่งวัตถุนี้ทำอะไร

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

@Test
public void canSaveData() {
    writeDataToDatabase();
    // what can you assert - the only expectation you can have here is that an exception was not thrown.
}

@Test
public void canReadData() {
    // how do I even get data in there to read if I cannot call the method which writes it?
}

ปัญหานี้เกิดขึ้นเนื่องจากมุมมองของวิธีการทดสอบ อย่าทดสอบวิธีการ ทดสอบพฤติกรรม พฤติกรรมของคลาส WidgetDao คืออะไร มันยังคงมีเครื่องมือ ตกลงคุณจะยืนยันได้อย่างไรว่ายังคงมีวิดเจ็ตอยู่? คำจำกัดความของการคงอยู่คืออะไร? หมายความว่าเมื่อคุณเขียนมันคุณสามารถอ่านได้อีกครั้ง ดังนั้นอ่าน + เขียนร่วมกันกลายเป็นการทดสอบและในความคิดของฉันการทดสอบที่มีความหมายมากขึ้น

@Test
public void widgetsCanBeStored() {
    Widget widget = new Widget();
    widget.setXXX.....
    // blah

    widgetDao.storeWidget(widget);
    Widget stored = widgetDao.getWidget(widget.getWidgetId());
    assertEquals(widget, stored);
}

นั่นคือตรรกะ, เหนียว, เชื่อถือได้และในความเห็นของฉันการทดสอบที่มีความหมาย

คำตอบอื่น ๆ จะเน้นว่าการแยกที่สำคัญคือการอภิปรายเชิงเปรียบเทียบกับการปฏิบัติจริงหรือไม่และการทดสอบหน่วยอาจจะหรือไม่สอบถามฐานข้อมูล สิ่งเหล่านี้ไม่ได้ตอบคำถามจริงๆ

ในการทดสอบว่าสามารถเก็บบางสิ่งได้หรือไม่คุณต้องเก็บมันแล้วอ่านกลับ คุณไม่สามารถทดสอบว่ามีบางสิ่งถูกเก็บไว้หรือไม่ถ้าคุณไม่ได้รับอนุญาตให้อ่าน อย่าทดสอบการจัดเก็บข้อมูลแยกต่างหากจากการดึงข้อมูล คุณจะจบลงด้วยการทดสอบที่ไม่ได้บอกอะไรคุณ


วิธีการที่ชาญฉลาดมากและอย่างที่คุณพูดฉันคิดว่าคุณโดนหนึ่งในข้อสงสัยหลักที่ฉันมี ขอบคุณ!
carlossierra

0

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

อีกทางหนึ่ง (และมองเห็นการละเมิดความรับผิดชอบเดียว) สมมติว่ามันจำเป็นต้องคงรุ่นของสตริงที่เข้ารหัส UTF-8 ไว้ในฟิลด์ไบต์ที่มุ่งเน้น แต่จริงๆแล้วยังคง Shift JIS ส่วนประกอบอื่น ๆ กำลังอ่านฐานข้อมูลและคาดว่าจะเห็น UTF-8 ดังนั้นความต้องการ จากนั้นการเดินทางไป - กลับผ่านวัตถุนี้จะรายงานชื่อและที่อยู่ที่ถูกต้องเพราะมันจะแปลงกลับจาก Shift JIS แต่การทดสอบของคุณไม่ถูกตรวจพบข้อผิดพลาด หวังว่ามันจะถูกตรวจพบโดยการทดสอบการรวมภายหลัง แต่จุดทดสอบหน่วยทั้งหมดคือการจับปัญหาก่อนและรู้ว่าส่วนประกอบที่รับผิดชอบ

หากหนึ่งในนั้นไม่ได้ทำในสิ่งที่มันควรจะเป็นกรณีทดสอบของตัวเองจะล้มเหลวและเราสามารถแก้ไขและเรียกใช้แบตเตอรี่ทดสอบอีกครั้ง

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

เบาะแสว่ามีปัญหาที่นี่คือว่าการทดสอบสองที่คาดคะเนกำลังทดสอบหน่วยที่แตกต่างกันจริงทำสิ่งเดียวกัน พวกเขาทั้งสองเรียก setter ตามด้วย getter จากนั้นตรวจสอบผลลัพธ์ว่าเป็นค่าดั้งเดิม แต่คุณต้องการทดสอบว่า setter ยังคงข้อมูลอยู่ไม่ใช่ว่า setter / getter pair ทำงานร่วมกัน เพื่อให้คุณรู้ว่ามีอะไรผิดปกติคุณแค่ต้องหาว่าอะไรและแก้ไขการทดสอบ

หากรหัสของคุณได้รับการออกแบบมาอย่างดีสำหรับการทดสอบหน่วยมีอย่างน้อยสองวิธีที่คุณสามารถทดสอบว่าข้อมูลได้รับการยืนยันอย่างถูกต้องตามวิธีการทดสอบหรือไม่:

  • จำลองอินเทอร์เฟซฐานข้อมูลและให้บันทึกจำลองของคุณว่ามีการเรียกใช้ฟังก์ชันที่เหมาะสมด้วยค่าที่คาดหวัง นี่เป็นการทดสอบวิธีที่มันควรจะเป็นและเป็นการทดสอบแบบคลาสสิก

  • ส่งผ่านฐานข้อมูลจริงด้วยความตั้งใจเดียวกันเพื่อบันทึกว่าข้อมูลได้รับการยืนยันอย่างถูกต้องหรือไม่ แต่แทนที่จะมีฟังก์ชั่นเยาะเย้ยที่เพิ่งพูดว่า "ใช่ฉันได้ข้อมูลที่ถูกต้อง" การทดสอบของคุณอ่านออกจากฐานข้อมูลโดยตรงและยืนยันว่าถูกต้อง นี่อาจไม่ใช่การทดสอบที่บริสุทธิ์ที่สุดเท่าที่จะเป็นไปได้เพราะเอ็นจิ้นฐานข้อมูลทั้งหมดค่อนข้างใหญ่ที่จะใช้ในการเขียนเยาะเย้ยที่มีโอกาสมากขึ้นที่ฉันจะมองเห็นความละเอียดอ่อนบางอย่างที่ทำให้การทดสอบผ่านแม้ว่าบางอย่างผิดปกติ ไม่ควรใช้การเชื่อมต่อฐานข้อมูลเดียวกันเพื่ออ่านตามที่ใช้ในการเขียนเพราะฉันอาจเห็นการทำธุรกรรมที่ปราศจากข้อผูกมัด) แต่มันทดสอบสิ่งที่ถูกต้องและอย่างน้อยคุณก็รู้ว่ามันแม่นยำ ใช้ส่วนต่อประสานฐานข้อมูลทั้งหมดโดยไม่ต้องเขียนรหัสจำลองใด ๆ !

ดังนั้นจึงเป็นเพียงรายละเอียดของการดำเนินการทดสอบไม่ว่าฉันจะอ่านข้อมูลจากฐานข้อมูลการทดสอบโดย JDBC หรือว่าฉันจำลองฐานข้อมูล ไม่ว่าจะด้วยวิธีใดประเด็นก็คือฉันสามารถทดสอบหน่วยได้ดีกว่าโดยแยกมันออกจากกันถ้าฉันยอมให้มันสมรู้ร่วมคิดกับวิธีการที่ไม่ถูกต้องอื่น ๆ ในชั้นเรียนเดียวกันเพื่อให้ดูถูกต้อง ดังนั้นฉันต้องการใช้วิธีการที่สะดวกสบายเพื่อตรวจสอบว่าข้อมูลที่ถูกต้องนั้นยังคงอยู่อยู่นอกเหนือจากความน่าเชื่อถือของส่วนประกอบที่มีวิธีการที่ฉันกำลังทดสอบ

หากรหัสของคุณไม่ได้ออกแบบมาอย่างดีสำหรับการทดสอบหน่วยคุณอาจไม่มีทางเลือกเนื่องจากวัตถุที่วิธีการทดสอบที่คุณต้องการอาจไม่ยอมรับฐานข้อมูลเป็นการอ้างอิงแบบฉีด ในกรณีนี้การอภิปรายเกี่ยวกับวิธีที่ดีที่สุดในการแยกหน่วยภายใต้การทดสอบให้เปลี่ยนเป็นการอภิปรายเกี่ยวกับความเป็นไปได้ที่จะแยกหน่วยที่อยู่ภายใต้การทดสอบออก บทสรุปก็เหมือนกัน หากคุณสามารถหลีกเลี่ยงการสมคบคิดระหว่างหน่วยที่ผิดปกติได้คุณต้องดำเนินการภายใต้เวลาที่มีอยู่และสิ่งอื่นใดที่คุณคิดว่าจะมีประสิทธิภาพมากขึ้นในการค้นหาความผิดพลาดในรหัส


0

นี่คือวิธีที่ฉันชอบที่จะทำ นี่เป็นเพียงตัวเลือกส่วนตัวเนื่องจากสิ่งนี้จะไม่ส่งผลกระทบต่อผลของผลิตภัณฑ์ แต่เป็นวิธีการผลิตเท่านั้น

สิ่งนี้ขึ้นอยู่กับความเป็นไปได้ที่จะเพิ่มค่าจำลองในฐานข้อมูลของคุณโดยไม่ต้องใช้แอปพลิเคชันที่คุณกำลังทดสอบ

สมมติว่าคุณมีการทดสอบสองแบบ: - การอ่านฐานข้อมูล B - แทรกลงในฐานข้อมูล (ขึ้นอยู่กับ A)

ถ้า A ผ่านคุณมั่นใจได้ว่าผลลัพธ์ของ B จะขึ้นอยู่กับชิ้นส่วนที่ทดสอบใน B และไม่ใช่การขึ้นต่อกัน หาก A ล้มเหลวคุณสามารถลบค่าลบเท็จสำหรับ B ข้อผิดพลาดอาจเป็น B หรือ A คุณไม่สามารถทราบได้อย่างแน่นอนจนกว่า A จะกลับสู่ความสำเร็จ

ฉันจะไม่พยายามเขียนข้อความค้นหาในการทดสอบหน่วยเนื่องจากคุณไม่ควรรู้โครงสร้างฐานข้อมูลเบื้องหลังแอปพลิเคชัน (หรืออาจเปลี่ยน) ความหมายกรณีทดสอบของคุณอาจล้มเหลวเนื่องจากรหัสของคุณเอง เว้นแต่คุณจะเขียนแบบทดสอบสำหรับการทดสอบ แต่ใครจะเป็นคนทดสอบการทดสอบสำหรับการทดสอบ ...


-1

ในที่สุดมันก็มาถึงสิ่งที่คุณต้องการทดสอบ

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

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

ในบางกรณีคุณต้องยืนยันว่าสิ่งของได้รับการยืนยันอย่างไร (อาจมีกระบวนการอื่นอาศัยข้อมูลที่อยู่ในรูปแบบที่ถูกต้องในฐานข้อมูลนั้น) ตอนนี้คุณไม่สามารถพึ่งจะได้รับการบันทึกที่ถูกต้องกลับมาจากคลาส แต่คุณต้องตรวจสอบว่ามันถูกเก็บไว้ในฐานข้อมูลอย่างถูกต้อง และวิธีที่ตรงที่สุดในการทำเช่นนั้นคือการสืบค้นฐานข้อมูลเอง

ตอนนี้คุณไม่เพียง แต่สนใจเรียนที่ยึดมั่นในสัญญาของมัน (สร้างจัดเก็บและดึงข้อมูล) แต่ยังรวมถึงวิธีการทำให้สำเร็จ (เช่นข้อมูลที่ยืนยันจะต้องปฏิบัติตามอินเทอร์เฟซอื่น) อย่างไรก็ตามตอนนี้การทดสอบขึ้นอยู่กับทั้งอินเตอร์เฟสคลาสและรูปแบบหน่วยเก็บข้อมูลดังนั้นจึงเป็นการยากที่จะนำกลับมาใช้ใหม่อย่างสมบูรณ์ (บางคนอาจเรียกการทดสอบประเภทนี้ว่า "การทดสอบการรวม")


@downvoters: คุณช่วยบอกเหตุผลของคุณกับฉันได้ไหมเพื่อที่ฉันจะได้ปรับปรุงด้านที่คุณคิดว่าคำตอบของฉันขาดไป?
hoffmale
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.