ฉันควรทดสอบวิธีการส่วนตัวหรือวิธีสาธารณะเท่านั้น [ปิด]


348

ฉันได้อ่านโพสต์นี้เกี่ยวกับวิธีทดสอบวิธีส่วนตัว ฉันมักจะไม่ทดสอบพวกเขาเพราะฉันมักจะคิดว่ามันเร็วกว่าที่จะทดสอบเฉพาะวิธีสาธารณะที่จะถูกเรียกจากนอกวัตถุ คุณทดสอบวิธีการส่วนตัวหรือไม่? ฉันควรทดสอบพวกเขาเสมอ



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

คำตอบ:


328

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

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


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

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

37
"วิธีการส่วนตัวคือรายละเอียดการนำไปปฏิบัติที่ควรซ่อนอยู่กับผู้ใช้ของชั้นเรียน" แต่การทดสอบนั้นอยู่ในด้านเดียวกันของอินเทอร์เฟซของคลาส 'เป็นผู้ใช้ "ปกติ" (รันไทม์) หรือไม่ ;)
mlvljr

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

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

293

วัตถุประสงค์ของการทดสอบคืออะไร?

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

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

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

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


72
ปัญหาคือที่นี่ "การเปลี่ยนแปลงโค้ดในอนาคต" นั้นหมายถึงการปรับโครงสร้างภายในของบางคลาส สิ่งนี้เกิดขึ้นบ่อยครั้งที่การทดสอบการเขียนสร้างอุปสรรคในการปรับโครงสร้างใหม่
Outlaw Programmer

40
นอกจากนี้หากคุณเปลี่ยนการทดสอบหน่วยของคุณอย่างต่อเนื่องแสดงว่าคุณสูญเสียความมั่นคงในการทดสอบและคุณอาจจะสร้างข้อบกพร่องในการทดสอบหน่วยด้วยตนเอง
17 จาก 26

6
@ 17 หากการทดสอบและการใช้งานมีการปรับเปลี่ยนแบบซิงโครนัส (ตามที่ควรจะเป็น) ควรจะมีปัญหาน้อยลง
mlvljr

11
@Sauronlord เหตุผลที่คุณทดสอบเมธอดไพรเวตนั้นเป็นเพราะถ้าคุณทดสอบเมธอดสาธารณะเมื่อการทดสอบล้มเหลวเราไม่ทราบโดยตรงว่าสาเหตุของความล้มเหลวนั้นเกิดจากที่ใด มันอาจจะเป็นทั้งในหรือtestDoSomething() testDoSomethingPrivate()สิ่งนี้ทำให้การทดสอบมีค่าน้อยลง . ต่อไปนี้เป็นเหตุผลเพิ่มเติมสำหรับการทดสอบstackoverflow.com/questions/34571/ส่วนบุคคลที่เป็นส่วนตัว :
Pacerier

3
@Pacerier นอกจากนี้ยังมีความแตกต่างระหว่างการทดสอบรหัสของคุณและมีกระบวนการทดสอบอัตโนมัติอย่างต่อเนื่อง คุณควรตรวจสอบให้แน่ใจว่าวิธีการส่วนตัวของคุณใช้งานได้ดี แต่คุณไม่ควรมีการทดสอบที่เชื่อมโยงกับวิธีการส่วนตัวเนื่องจากไม่ใช่กรณีการใช้งานของซอฟต์แวร์
Didier A.

150

ฉันมักจะทำตามคำแนะนำของ Dave Thomas และ Andy Hunt ในหนังสือการทดสอบ Pragmatic Unitของพวกเขา:

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

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


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

61

ฉันรู้สึกถูกบังคับให้ต้องทดสอบฟังก์ชั่นส่วนตัวเมื่อฉันทำตามคำแนะนำ QA ล่าสุดของเรามากขึ้นเรื่อย ๆ ในโครงการของเรา:

ไม่เกิน 10 ในความซับซ้อนของวงจรต่อฟังก์ชัน

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

นั่นคือเจ๋งจริงเพราะ callstack ตอนนี้อ่านง่ายกว่ามาก (แทนที่จะเป็นบั๊กในฟังก์ชั่นใหญ่ฉันมีข้อผิดพลาดในฟังก์ชั่นย่อยที่มีชื่อฟังก์ชั่นก่อนหน้าใน callstack เพื่อช่วยให้ฉันเข้าใจ 'ฉันไปถึงที่นั่นได้อย่างไร')

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

แค่ 2 เซ็นต์ของฉัน


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

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

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

สำหรับผู้ที่สนใจในความซับซ้อนของวัฏจักรฉันเพิ่มคำถามในหัวข้อ: stackoverflow.com/questions/105852/ …
VonC

อ๊ะ URL ของคำถามเปลี่ยนไปเนื่องจากมีการพิมพ์ผิดในชื่อ! stackoverflow.com/questions/105852/…
VonC

51

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

ทำไมเราถึงมีวิธีส่วนตัว

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

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

เราคิดว่าที่นี่มี 3 โซลูชั่น:

1. คุณสามารถทดสอบสิทธิ์ของคุณผ่านวิธีสาธารณะของคุณ

ข้อดี

  • การทดสอบหน่วยที่ตรงไปตรงมา (ไม่จำเป็นต้องมี 'แฮ็ก')

ข้อเสีย

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

2. ถ้าความเป็นส่วนตัวมีความสำคัญมากบางทีมันอาจเป็นรหัสสำหรับสร้างคลาสแยกใหม่สำหรับมัน

ข้อดี

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

ข้อเสีย

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

3. เปลี่ยนโมดิฟายเออร์สำหรับการเข้าถึงเป็นที่ป้องกัน

ข้อดี

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

ข้อเสีย

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

ตัวอย่าง

class Detective {
  public function investigate() {}
  private function sleepWithSuspect($suspect) {}
}
Altered version:
class Detective {
  public function investigate() {}
  final protected function sleepWithSuspect($suspect) {}
}
In Test class:
class Mock_Detective extends Detective {

  public test_sleepWithSuspect($suspect) 
  {
    //this is now accessible, but still not overridable!
    $this->sleepWithSuspect($suspect);
  }
}

ดังนั้นตอนนี้หน่วยทดสอบของเราสามารถเรียก test_sleepWithSuspect เพื่อทดสอบฟังก์ชันส่วนตัวในอดีตของเรา


eddy147 ฉันจริงๆเช่นแนวคิดของการทดสอบวิธีการป้องกันผ่าน mocks ขอบคุณ !!!!
Theodore R. Smith

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

@Matt หน่วยการทำงานสามารถชี้ไปที่คลาส แต่ยังเป็นวิธีการเดียว
eddy147

4
@ eddy147 การทดสอบหน่วยมาพร้อมกับการทดสอบการพัฒนาที่หน่วยถูกกำหนดให้เป็นคลาส เช่นเดียวกับ The Internets ความหมายได้ขยายออกไปเพื่อหมายถึงสิ่งต่าง ๆ มากมาย (เช่นถาม 2 คนว่าความแตกต่างระหว่างการทดสอบหน่วยกับการรวมเข้าด้วยกันคืออะไรและคุณจะได้รับคำตอบ 7 ข้อ) TDD นั้นหมายถึงวิธีการเขียนซอฟต์แวร์ด้วยหลักการ SOLID รวมถึง Single Responsibility ซึ่งคลาสมีความรับผิดชอบเดียวและไม่ควรมีความซับซ้อนสูงใน TDD คุณเขียนคลาสและทดสอบร่วมกันทั้งหน่วย วิธีการส่วนตัวถูกห่อหุ้มไม่มีการทดสอบหน่วยที่เกี่ยวข้อง
Matt Quigley

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

41

ฉันไม่ชอบการทดสอบการใช้งานส่วนตัวด้วยเหตุผลสองประการ พวกเขามีดังนี้ (เหล่านี้เป็นประเด็นหลักสำหรับคน TLDR):

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

ฉันจะอธิบายแต่ละข้อด้วยตัวอย่างที่เป็นรูปธรรม ปรากฎว่า 2) และ 3) มีการเชื่อมต่อที่ค่อนข้างซับซ้อนดังนั้นตัวอย่างของพวกเขาก็คล้ายกันแม้ว่าฉันจะพิจารณาเหตุผลแยกกันว่าทำไมคุณไม่ควรทดสอบวิธีการส่วนตัว

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

ฉันยังอธิบายด้วยว่าทำไม TDD ไม่ใช่ข้อแก้ตัวที่ถูกต้องสำหรับการทดสอบวิธีการส่วนตัวในตอนท้าย

ปรับวิธีการของคุณออกจากการออกแบบที่ไม่ดี

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

Rule Evaluator

ตัวอย่างเช่นคุณอาจต้องการทดสอบGetNextToken()โดยการเรียกมันบนสตริงอย่างต่อเนื่องและเห็นว่ามันจะส่งกลับผลลัพธ์ที่คาดหวัง ฟังก์ชั่นเช่นนี้รับประกันการทดสอบ: พฤติกรรมนั้นไม่สำคัญโดยเฉพาะถ้ากฎการโทเค็นของคุณซับซ้อน มาทำเป็นว่ามันไม่ซับซ้อนทั้งหมดและเราแค่ต้องการผูกเชือกในโทเค็นที่คั่นด้วยช่องว่าง ดังนั้นคุณเขียนแบบทดสอบบางทีมันอาจจะเป็นแบบนี้ (บางภาษาไม่เชื่อเรื่องรหัส psuedo หวังว่าไอเดียจะชัดเจน):

TEST_THAT(RuleEvaluator, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    re = RuleEvaluator(input_string);

    ASSERT re.GetNextToken() IS "1";
    ASSERT re.GetNextToken() IS "2";
    ASSERT re.GetNextToken() IS "test";
    ASSERT re.GetNextToken() IS "bar";
    ASSERT re.HasMoreTokens() IS FALSE;
}

นั่นดูดีจริงๆ เราต้องการให้แน่ใจว่าเรารักษาพฤติกรรมนี้ในขณะที่เราทำการเปลี่ยนแปลง แต่GetNextToken()เป็นฟังก์ชั่นส่วนตัว ! ดังนั้นเราจึงไม่สามารถทดสอบได้เช่นนี้เพราะจะไม่รวบรวม (สมมติว่าเราใช้ภาษาที่บังคับให้เป็นสาธารณะ / ส่วนตัวซึ่งแตกต่างจากภาษาสคริปต์บางอย่างเช่น Python) แต่สิ่งที่เกี่ยวกับการเปลี่ยนRuleEvaluatorชั้นเรียนให้เป็นไปตามหลักการความรับผิดชอบเดียว (หลักการความรับผิดชอบเดียว) ตัวอย่างเช่นเรามี parser, tokenizer และตัวประเมินผลติดอยู่ในคลาสเดียว มันจะดีกว่าไหมถ้าแยกความรับผิดชอบเหล่านั้นออก? ด้านบนของที่ถ้าคุณสร้างTokenizerชั้นเรียนแล้วก็วิธีการสาธารณะจะเป็นและHasMoreTokens() ชั้นอาจมีGetNextTokens()RuleEvaluatorTokenizerวัตถุในฐานะสมาชิก ตอนนี้เราสามารถทำการทดสอบแบบเดียวกันกับข้างต้นได้ยกเว้นว่าเราจะทำการทดสอบTokenizerคลาสแทนRuleEvaluatorคลาส

นี่คือสิ่งที่ดูเหมือนใน UML:

Refactored Rule Evaluator

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

การทดสอบจะมีลักษณะคล้ายกันมากยกเว้นจะรวบรวมในเวลานี้เนื่องจากGetNextToken()วิธีการนี้เป็นแบบสาธารณะในTokenizerชั้นเรียน:

TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);

    ASSERT tokenizer.GetNextToken() IS "1";
    ASSERT tokenizer.GetNextToken() IS "2";
    ASSERT tokenizer.GetNextToken() IS "test";
    ASSERT tokenizer.GetNextToken() IS "bar";
    ASSERT tokenizer.HasMoreTokens() IS FALSE;
}

การทดสอบส่วนประกอบส่วนตัวผ่านส่วนต่อประสานสาธารณะและหลีกเลี่ยงการทำซ้ำการทดสอบ

แม้ว่าคุณจะไม่คิดว่าคุณสามารถแบ่งปัญหาของคุณออกเป็นส่วนประกอบแบบแยกส่วนได้น้อยลง (ซึ่งคุณสามารถทำได้ 95% ของเวลาถ้าคุณลองทำ) คุณสามารถทดสอบฟังก์ชั่นส่วนตัวผ่านทางส่วนต่อประสานสาธารณะได้ หลายครั้งที่สมาชิกส่วนตัวไม่คุ้มค่าการทดสอบเพราะพวกเขาจะถูกทดสอบผ่านส่วนต่อประสานสาธารณะ หลายครั้งที่ฉันเห็นคือการทดสอบที่มีลักษณะคล้ายกันมากแต่ทดสอบฟังก์ชัน / วิธีการที่ต่างกันสองวิธี สิ่งที่จบลงด้วยการเกิดขึ้นคือเมื่อมีการเปลี่ยนแปลงความต้องการ (และพวกเขามักจะทำ) ขณะนี้คุณมี 2 ทดสอบเสียแทน 1. และถ้าคุณผ่านการทดสอบจริงๆวิธีการส่วนตัวทั้งหมดของคุณคุณอาจมีมากขึ้นเช่นการทดสอบ 10 เสียแทน 1. ในระยะสั้น ทดสอบฟังก์ชั่นส่วนตัว (โดยใช้FRIEND_TESTหรือทำให้ประชาชนพวกเขาหรือใช้สะท้อน) ที่อาจได้รับการทดสอบผ่านอินเตอร์เฟซที่สาธารณะอาจทำให้เกิดความซ้ำซ้อนในการทดสอบ คุณไม่ต้องการสิ่งนี้เพราะไม่มีอะไรทำให้เจ็บปวดมากกว่าชุดทดสอบที่ทำให้คุณทำงานช้าลง มันควรจะลดเวลาในการพัฒนาและลดค่าบำรุงรักษา! หากคุณทดสอบวิธีการส่วนตัวที่ทดสอบเป็นอย่างอื่นผ่านทางส่วนต่อประสานสาธารณะชุดทดสอบอาจทำตรงข้ามได้เป็นอย่างดีและเพิ่มค่าใช้จ่ายในการบำรุงรักษาและเพิ่มเวลาในการพัฒนา เมื่อคุณทำให้ฟังก์ชั่นส่วนตัวเป็นที่เปิดเผยต่อสาธารณชนหรือถ้าคุณใช้สิ่งที่ชอบFRIEND_TESTและ / หรือไตร่ตรองคุณจะต้องเสียใจในระยะยาว

พิจารณาการนำไปใช้ที่เป็นไปได้ของTokenizerคลาส:

ป้อนคำอธิบายรูปภาพที่นี่

สมมติว่าSplitUpByDelimiter()มีหน้าที่คืนค่าอาเรย์โดยที่แต่ละองค์ประกอบในอาเรย์นั้นเป็นโทเค็น ยิ่งกว่านั้นสมมุติว่าGetNextToken()มันเป็นตัววนซ้ำของเวกเตอร์นี้ ดังนั้นการทดสอบสาธารณะของคุณอาจมีลักษณะเช่นนี้:

TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);

    ASSERT tokenizer.GetNextToken() IS "1";
    ASSERT tokenizer.GetNextToken() IS "2";
    ASSERT tokenizer.GetNextToken() IS "test";
    ASSERT tokenizer.GetNextToken() IS "bar";
    ASSERT tokenizer.HasMoreTokens() IS false;
}

ลองแกล้งทำเป็นว่าเรามีสิ่งที่ไมเคิลโทรขนของเครื่องมือคล้า นี่คือเครื่องมือที่ช่วยให้คุณสัมผัสชิ้นส่วนส่วนตัวของคนอื่น ตัวอย่างFRIEND_TESTมาจาก googletest หรือไตร่ตรองหากภาษานั้นรองรับ

TEST_THAT(TokenizerTest, canGenerateSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);
    result_array = tokenizer.SplitUpByDelimiter(" ");

    ASSERT result.size() IS 4;
    ASSERT result[0] IS "1";
    ASSERT result[1] IS "2";
    ASSERT result[2] IS "test";
    ASSERT result[3] IS "bar";
}

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

เมื่อใดที่การทดสอบวิธีการส่วนตัวจะเหมาะสม

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

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

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

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

ข้อแก้ตัวของ TDD

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

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

บันทึก:

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

ป.ล. นี่คือการบรรยายที่เกี่ยวข้องเกี่ยวกับคลาสภูเขาน้ำแข็งและเครื่องมือการคล้าหาโดย Michael Feathers: https://www.youtube.com/watch?v=4cVZvoFGJTU


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

ฉันเข้าใจสิ่งที่คุณกำลังพูด แต่ชอบวิธีที่ชุมชน Python จัดการกับปัญหานั้น หากคุณตั้งชื่อสมาชิก "ส่วนตัว" ที่มีปัญหากับผู้นำ_ก็จะส่งสัญญาณ "เฮ้นี่คือ" ส่วนตัว "คุณสามารถใช้งานได้ แต่การเปิดเผยแบบเต็มมันไม่ได้ถูกออกแบบมาเพื่อนำมาใช้ซ้ำและคุณควรใช้เฉพาะในกรณีที่คุณจริงๆรู้ว่าคุณกำลังทำอะไรอยู่ ". คุณอาจจะใช้วิธีการเดียวกันในภาษาใด ๆ : ทำให้บรรดาสมาชิกที่สาธารณะ _แต่พวกเขาทำกับชั้นนำ หรือบางทีฟังก์ชั่นเหล่านั้นควรเป็นแบบส่วนตัวและเพิ่งทดสอบผ่านส่วนต่อประสานสาธารณะ (ดูคำตอบสำหรับรายละเอียดเพิ่มเติม) เป็นกรณี ๆ ไปไม่มีกฎทั่วไป
Matt Messersmith

26

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

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


21

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


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

1
ด้วยเหตุผลเดียวที่สามารถโต้แย้งได้ว่ามีเพียงการทดสอบซอฟต์แวร์จากส่วนต่อประสานผู้ใช้ (การทดสอบระดับระบบ) เพราะอย่างใดทุกฟังก์ชั่นในซอฟต์แวร์จะถูกดำเนินการจากที่นั่น
Dirk Herrmann

18

หากวิธีการส่วนตัวถูกกำหนดไว้อย่างดี (เช่นมันมีฟังก์ชั่นที่สามารถทดสอบได้และไม่ได้หมายถึงการเปลี่ยนแปลงตลอดเวลา) ใช่แล้ว ฉันทดสอบทุกอย่างที่สามารถทดสอบได้ในที่ที่เหมาะสม

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

มันเร็วแก้จุดบกพร่องในภายหลัง

อดัม


1
ในกรณีนี้มันจะไม่เหมาะสมที่จะย้ายวิธีการส่วนตัวไปยังชั้นเรียนอื่นแล้วทำให้เป็นสาธารณะหรือสาธารณะคงที่
Outlaw Programmer

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

12

หากคุณกำลังพัฒนาแบบทดสอบขับเคลื่อน (TDD) คุณจะทดสอบวิธีการส่วนตัวของคุณ


2
คุณจะแยกวิธีการส่วนตัวเมื่อ refactoring agiletips.blogspot.com/2008/11/…
Josh Johnson

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

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

ไม่เห็นด้วยโปรดดูคำตอบของฉันด้านล่าง
Matt Messersmith

11

ฉันไม่ใช่ผู้เชี่ยวชาญในด้านนี้ แต่การทดสอบหน่วยควรทดสอบพฤติกรรมไม่ใช่การนำไปใช้ วิธีการส่วนตัวเป็นส่วนหนึ่งของการดำเนินการอย่างเคร่งครัดดังนั้นไม่ควรทดสอบ IMHO


ทดสอบการใช้งานอยู่ที่ไหน? หากฟังก์ชั่นบางอย่างใช้การแคชนี่คือรายละเอียดการใช้งานและการแคชไม่ได้รับการทดสอบหรือไม่?
Dirk Herrmann

11

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

คำตอบของ Trumpi ต่อโพสต์ที่คุณเชื่อมโยงนั้นเป็นคำตอบที่ดีที่สุด


9

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


7

ฉันเจอปัญหานี้มาระยะหนึ่งแล้วโดยเฉพาะอย่างยิ่งกับการลองใช้มือที่ TDD

ฉันเจอโพสต์สองที่ฉันคิดว่าปัญหานี้อย่างละเอียดพอในกรณีของ TDD

  1. การทดสอบวิธีการส่วนตัว TDD และการทดสอบการขับเคลื่อนอีกครั้ง
  2. การพัฒนาที่ขับเคลื่อนด้วยการทดสอบไม่ใช่การทดสอบ

สรุป:

  • เมื่อใช้เทคนิคการพัฒนาแบบทดสอบขับเคลื่อน (ออกแบบ), วิธีการส่วนตัวควรเกิดขึ้นเฉพาะในระหว่างกระบวนการ re-factoring ของรหัสที่ทำงานแล้วและทดสอบแล้ว

  • โดยธรรมชาติของกระบวนการบิตใด ๆ ของฟังก์ชั่นการใช้งานที่เรียบง่ายที่ดึงออกมาจากฟังก์ชั่นการทดสอบอย่างละเอียดจะไม่ว่าจะเป็นการทดสอบด้วยตนเอง (เช่นการทดสอบทางอ้อมครอบคลุม)

สำหรับฉันแล้วดูเหมือนชัดเจนพอที่ในตอนเริ่มต้นของการเข้ารหัสวิธีส่วนใหญ่จะเป็นฟังก์ชั่นระดับที่สูงขึ้นเพราะพวกเขากำลังห่อหุ้ม / อธิบายการออกแบบ

ดังนั้นวิธีการเหล่านี้จะเป็นแบบสาธารณะและทดสอบได้ง่ายพอ

วิธีการภาคเอกชนจะมาในภายหลังเมื่อทุกอย่างทำงานได้ดีและเรามีอีกครั้งแฟเพื่อประโยชน์ของการอ่านและการรักษาความสะอาด


6

ตามที่ยกมาข้างต้น "ถ้าคุณไม่ทดสอบวิธีส่วนตัวของคุณคุณจะรู้ได้อย่างไรว่าพวกเขาจะไม่แตก"

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

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

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

ดังนั้นโดยสรุปใช่หน่วยทดสอบวิธีส่วนตัวของคุณ


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

6

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


3

หากคุณไม่ทดสอบวิธีส่วนตัวของคุณคุณจะรู้ได้อย่างไรว่าพวกเขาจะไม่หยุดพัก


19
โดยการเขียนผ่านการทดสอบวิธีการสาธารณะของคุณ
scubabbl

3
วิธีการส่วนตัวเหล่านั้นจะถูกเรียกโดยวิธีสาธารณะของชั้นเรียน ดังนั้นเพียงทดสอบวิธีสาธารณะที่เรียกว่าวิธีส่วนตัว
Jop

1
หากวิธีสาธารณะของคุณทำงานอย่างถูกต้องชัดเจนว่าวิธีการส่วนตัวที่พวกเขาเข้าถึงทำงานอย่างถูกต้อง
17 จาก 26

หากการทดสอบวิธีสาธารณะของคุณล้มเหลวคุณจะรู้ได้ทันทีว่ามีบางสิ่งที่ไม่ถูกต้องในระดับที่ต่ำกว่าในวัตถุ / ส่วนประกอบ / ฯลฯ
Rob

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

2

เห็นได้ชัดว่ามันขึ้นอยู่กับภาษา ในอดีตด้วย c ++ ฉันได้ประกาศคลาสทดสอบให้เป็นคลาสเพื่อน น่าเสียดายที่ต้องใช้รหัสการผลิตของคุณเพื่อทราบเกี่ยวกับคลาสทดสอบ


5
คำหลักเพื่อนทำให้ฉันเศร้า
Rob

นี่ไม่ใช่ปัญหาหากมีการใช้คลาสการทดสอบในโครงการอื่น สิ่งสำคัญคือรหัสการผลิตไม่ได้อ้างอิงคลาสการทดสอบ
Olumide

2

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

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

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

สำหรับฉันแล้วพรมแดนระหว่างวิธีการส่วนตัวและสาธารณะเป็นเกณฑ์ด้านจิตวิทยาเมื่อพูดถึงการทดสอบ เกณฑ์ที่สำคัญสำหรับฉันคือ:

  • เป็นวิธีการที่เรียกว่ามากกว่าหนึ่งครั้งจากสถานที่ที่แตกต่างกัน?
  • วิธีการที่ซับซ้อนพอที่จะต้องมีการทดสอบ?

1

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


1

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

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

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


1

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

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

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

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

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


0

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


1
@VisibleForTesting เป็นคำอธิบายประกอบสำหรับสิ่งนั้น ฉันไม่ได้ผ่อนคลายการห่อหุ้มเพื่อการทดสอบให้ใช้ dp4j.com
simpatico

0

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


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

0

คำตอบของ "ฉันควรทดสอบวิธีการส่วนตัวหรือไม่" คือ "....... บางครั้ง" โดยทั่วไปคุณควรทดสอบกับอินเทอร์เฟซของคลาสของคุณ

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

นี่คือตัวอย่าง:

class Thing
  def some_string
    one + two
  end

  private 

  def one
    'aaaa'
  end

  def two
    'bbbb'
  end

end


class RefactoredThing
def some_string
    one + one_a + two + two_b
  end

  private 

  def one
    'aa'
  end

  def one_a
    'aa'
  end

  def two
    'bb'
  end

  def two_b
    'bb'
  end
end

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

def some_string_positioner
  if some case
  elsif other case
  elsif other case
  elsif other case
  else one more case
  end
end

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

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


0

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


0

ประเด็นหลักอย่างหนึ่งก็คือ

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

การทดสอบการเขียนตามการมองเห็นของวิธีการเป็นความคิดที่ไม่เกี่ยวข้องอย่างสมบูรณ์

โดยตรงกันข้าม

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

แม้ว่าจะมีเครื่องมือบางอย่างเช่นPower Mockที่รองรับสิ่งนั้น แต่มันเป็นการทำงานที่อันตราย เหตุผลก็คือต้องทำการแฮ็ค JVM เพื่อให้บรรลุ

วิธีแก้ปัญหาหนึ่งที่สามารถทำได้คือ (ถ้าคุณต้องการเขียนกรณีทดสอบสำหรับวิธีส่วนตัว)

ประกาศผู้ส่วนตัววิธีการตามที่ได้รับการคุ้มครอง แต่อาจไม่สะดวกสำหรับหลาย ๆ สถานการณ์


0

ไม่เพียงเกี่ยวกับวิธีการหรือฟังก์ชั่นสาธารณะหรือส่วนตัว แต่ยังเกี่ยวกับรายละเอียดการใช้งาน ฟังก์ชั่นส่วนตัวเป็นเพียงส่วนหนึ่งของรายละเอียดการใช้งาน

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

A) ใช่คุณควรทดสอบรายละเอียดการใช้งาน:

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

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

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

B) วิธีทดสอบรายละเอียดการใช้งาน

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

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

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

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

ถึงกระนั้นก็เป็นกฎที่ดีที่จะใช้ "ประตูหน้าแรก" (ดูhttp://xunitpatterns.com/Principles%20of%20Test%20Automation.html ) แต่โปรดจำไว้ว่ามันถูกเรียกว่า "ประตูหน้าแรก" และไม่ใช่ "ประตูหน้าเท่านั้น"

C) สรุป

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


0

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

ลองพิจารณาตัวอย่างที่มีการประดิษฐ์เล็กน้อยต่อไปนี้ สมมติว่าเราต้องการแสดงฟังก์ชั่นต่อสาธารณะที่ใช้จำนวนเต็ม 3 จำนวนและส่งกลับค่าจริงถ้าหากจำนวนเต็ม 3 ตัวนั้นเป็นจำนวนเฉพาะทั้งหมด เราอาจนำไปใช้เช่นนี้:

public bool allPrime(int a, int b, int c)
{
  return andAll(isPrime(a), isPrime(b), isPrime(c))
}

private bool andAll(bool... boolArray)
{
  foreach (bool b in boolArray)
  {
    if(b == false) return false;
  }
  return true;
}

private bool isPrime(int x){
  //Implementation to go here. Sorry if you were expecting a prime sieve.
}

ตอนนี้ถ้าเราจะใช้วิธีการอย่างเข้มงวดว่ามีเพียงฟังก์ชั่นของประชาชนควรจะทดสอบเราเคยได้รับอนุญาตให้ทดสอบallPrimeและไม่ได้หรือisPrimeandAll

< 0ในฐานะที่เป็นผู้ทดสอบเราอาจจะสนใจในห้าเป็นไปได้สำหรับแต่ละอาร์กิวเมนต์: = 0, = 1, prime > 1, not prime > 1, แต่เพื่อให้ละเอียดเราต้องดูว่าการรวมกันของการโต้เถียงเล่นด้วยกันอย่างไร นั่นคือ5*5*5= 125 กรณีทดสอบที่เราต้องการทดสอบฟังก์ชั่นนี้อย่างละเอียดตามสัญชาติญาณของเรา

ในทางกลับกันถ้าเราได้รับอนุญาตให้ทดสอบฟังก์ชั่นส่วนตัวเราสามารถครอบคลุมพื้นที่ได้มากโดยมีกรณีทดสอบน้อยลง เราต้องการเพียง 5 กรณีทดสอบisPrimeเพื่อทดสอบในระดับเดียวกับสัญชาตญาณก่อนหน้าของเรา และด้วยสมมติฐานขอบเขตขนาดเล็กที่เสนอโดย Daniel Jackson เราแค่ต้องการทดสอบandAllฟังก์ชั่นที่มีความยาวน้อยเช่น 3 หรือ 4 ซึ่งจะเป็นการทดสอบอีก 16 ครั้ง ดังนั้นทั้งหมด 21 การทดสอบ แทนที่จะเป็น 125 แน่นอนเราอาจต้องการทดสอบสองสามครั้งallPrimeแต่เราจะไม่รู้สึกว่าจำเป็นต้องครอบคลุมชุดข้อมูลสถานการณ์การป้อนข้อมูลทั้งหมด 125 ชุดที่เราบอกว่าเราใส่ใจ เพียงไม่กี่เส้นทางที่มีความสุข

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


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

แมตต์ใช่ตัวอย่างไม่เหมาะฉันจะให้คุณ แต่หลักการควรชัดเจน
Colm Bhandal

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

0

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

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