ทำไมการทดสอบหน่วยวิธีส่วนตัวจึงถือว่าเป็นการปฏิบัติที่ไม่ดี?


18

บริบท:

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

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

ปัญหา:

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

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

คำถาม:

ฉันสนใจเกี่ยวกับการปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดและต้องการที่จะเข้าใจ:

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

ความแม่นยำ:

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


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

@ JohnWu: ฉันรู้ถึงความแตกต่างระหว่างการทดสอบ White-box และ Black-box แต่ถึงแม้จะอยู่ในการทดสอบกล่องสีขาวดูเหมือนว่าความจำเป็นในการทดสอบวิธีการส่วนตัวเป็นคำแนะนำสำหรับปัญหาการออกแบบ คำถามของฉันคือความพยายามที่จะเข้าใจว่าอะไรคือเส้นทางที่ดีที่สุดเมื่อฉันล้มลงที่นั่น ...
Serge Ballesta

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

@SergeBallesta กล่าวอีกนัยหนึ่งอ้างอิงถึงบทความที่ทำให้คุณเชื่อว่าการทดสอบวิธีการส่วนตัวเป็นการปฏิบัติที่ไม่ดี จากนั้นอธิบายให้เราทราบว่าทำไมคุณถึงเชื่อ
Laiv

คำตอบ:


18

เหตุผลสองประการ:

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

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

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

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

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

ฉันมีคำตอบที่อธิบายรายละเอียดเกี่ยวกับ SO ที่ฉันจะไม่ทำซ้ำที่นี่: /programming/105007/should-i-test-private-methods-or-only-public-ones/ 47401015 # 47401015


4
เหตุผลที่ 1: คลุมเครือ เหตุผลที่ 2: จะเกิดอะไรขึ้นถ้าวิธีช่วยเหลือส่วนตัวของคุณไม่ควรเป็นส่วนหนึ่งของ API สาธารณะ เหตุผลที่ 3: ไม่ใช่ถ้าคุณออกแบบชั้นเรียนของคุณอย่างถูกต้อง เคล็ดลับสุดท้ายของคุณ: ทำไมฉันจะลบการทดสอบที่ดีอย่างสมบูรณ์แบบที่พิสูจน์ว่าวิธีการที่ฉันเขียนได้ผล?
Robert Harvey

4
@RobertHarvey เหตุผลที่ 2: เข้าถึงได้ทางอ้อมผ่าน API สาธารณะ! = เป็นส่วนหนึ่งของ API สาธารณะ หากฟังก์ชั่นส่วนตัวของคุณไม่สามารถทดสอบได้ผ่าน API สาธารณะอาจเป็นรหัสที่ไม่ทำงานและควรลบออกหรือไม่ หรือชั้นเรียนของคุณเป็นภูเขาน้ำแข็ง (เหตุผลที่ 1) และควรได้รับการปรับปรุงใหม่
Frax

5
@RobertHarvey หากคุณไม่สามารถทดสอบฟังก์ชั่นส่วนตัวผ่าน API สาธารณะได้ให้ลบออกเพราะไม่มีวัตถุประสงค์ที่เป็นประโยชน์
David Arno

1
@RobertHarvey 1: กลิ่นการออกแบบมักจะเป็นอัตนัย / ไม่แน่นอนดังนั้นแน่นอน แต่ฉันได้แสดงตัวอย่างที่เป็นรูปธรรมเกี่ยวกับรูปแบบการต่อต้านและมีรายละเอียดเพิ่มเติมในคำตอบ SO ของฉัน 2: วิธีการส่วนตัวไม่สามารถเป็นส่วนหนึ่งของ API สาธารณะได้ (โดยคำจำกัดความ: เป็นวิธีส่วนตัว) ... ดังนั้นฉันไม่คิดว่าคำถามของคุณเหมาะสมนัก ฉันพยายามที่จะได้รับจุดที่ว่าถ้าคุณมีสิ่งที่ชอบbin_search(arr, item)(สาธารณะ) และbin_search(arr, item, low, hi)(ส่วนตัวมีหลายวิธีที่จะทำการค้นหาถังขยะ) แล้วสิ่งที่คุณต้องทดสอบคือประชาชนหันหน้าไปทางหนึ่ง ( bin_search(arr, item))
Matt Messersmith

1
@RobertHarvey 3: ประการแรกผมบอกว่ามีความเสี่ยงไม่รับประกัน ประการที่สองการอ้างว่า "ใช้งานได้หากคุณทำอย่างถูกต้อง" เป็นการตอบสนองด้วยตนเอง ตัวอย่างเช่น "คุณสามารถเขียนระบบปฏิบัติการในฟังก์ชั่นเดียวถ้าคุณทำอย่างถูกต้อง " สิ่งนี้ไม่เป็นเท็จ แต่ก็ไม่มีประโยชน์เช่นกัน เกี่ยวกับเคล็ดลับ: คุณจะลบเพราะหากการดำเนินการของคุณเปลี่ยนแปลง (เช่นคุณต้องการสลับการใช้งานแบบส่วนตัว) ชุดทดสอบของคุณจะเข้าสู่ทางของคุณ (คุณจะมีการทดสอบที่ล้มเหลวซึ่งคุณไม่ควรทำ)
Matt Messersmith

14

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


ไม่แน่ใจว่าทำไมคำตอบของคุณถูกลดระดับลง มันสั้นไปยังจุดและได้รับคำตอบที่ถูกต้อง 100%
David Arno

3
@DavidArno: อาจเป็นเพราะการทดสอบวิธีการส่วนตัวไม่ได้เกี่ยวข้องกับการทดสอบความละเอียดมากนัก มันมีทุกอย่างเกี่ยวกับการมีเพศสัมพันธ์กับรายละเอียดการใช้งาน
Robert Harvey

11

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

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

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

มีความตึงเครียดตามธรรมชาติระหว่างการทดสอบหน่วยและการพัฒนาที่เพิ่มขึ้น นักพัฒนาซอฟต์แวร์ที่ใช้ REPL (read-eval-print loop) สามารถยืนยันได้ว่าจะให้ประสิทธิภาพในการเขียนและทดสอบการใช้งานเล็กน้อยในขณะที่คุณ "เพิ่ม" คลาสหรือฟังก์ชัน วิธีเดียวที่ดีในการทำเช่นนั้นในสภาพแวดล้อมที่ใช้การทดสอบหน่วยคือการเขียนการทดสอบหน่วยสำหรับวิธีการส่วนตัว แต่มีแรงเสียดทานจำนวนมากในการทำเช่นนั้น การทดสอบหน่วยใช้เวลาในการเขียนคุณต้องใช้วิธีการที่แท้จริงในการทดสอบและกรอบการทดสอบของคุณต้องสนับสนุนความสามารถในการรักษาวิธีการส่วนตัวเพื่อไม่ให้เกิดมลภาวะ API ภายนอกของคุณ

ระบบนิเวศบางระบบเช่น C # และ. NET มีวิธีในการสร้างสภาพแวดล้อมที่คล้าย REPL (เครื่องมือเช่น Linqpad ทำสิ่งนี้) แต่ยูทิลิตี้เหล่านั้นมี จำกัด เนื่องจากคุณไม่สามารถเข้าถึงโครงการของคุณได้ หน้าต่างทันทีใน Visual Studio ไม่สะดวก มันยังไม่มี Intellisense ที่สมบูรณ์คุณต้องใช้ชื่อที่มีคุณสมบัติครบถ้วนในนั้นและจะทริกเกอร์บิลด์ทุกครั้งที่คุณใช้


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

3
@DavidArno: [ยัก] ฉันทำไปซักพักแล้ว การทดสอบหน่วยสำหรับวิธีส่วนตัวนั้นใช้งานได้ดีสำหรับฉันจนกระทั่ง Microsoft ตัดสินใจหยุดสนับสนุนวัตถุพร็อกซีในกรอบการทดสอบ ไม่มีอะไรเกิดขึ้นเนื่องจากการระเบิด ฉันไม่เคยฉีกหลุมในจักรวาลด้วยการเขียนวิธีทดสอบส่วนตัว
Robert Harvey

2
@DavidArno: ทำไมฉันถึงเลิกใช้เทคนิคที่ดีอย่างสมบูรณ์แบบที่ให้ประโยชน์กับฉันเพียงเพราะบางคนบนอินเทอร์เน็ตบอกว่ามันเป็นความคิดที่ไม่ดีโดยไม่ต้องให้เหตุผลอะไรเลย?
Robert Harvey

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

2
@Frax แน่นอนฉันทำได้ แต่ด้วยตรรกะนั้นฉันควรยกเลิกการทดสอบหน่วยเพื่อสนับสนุนการทดสอบการรวมระบบทั้งระบบ ท้ายที่สุด "ในกรณีส่วนใหญ่คุณควรแก้ไขการทดสอบเหล่านี้เพื่อทดสอบพฤติกรรมเดียวกัน"
Alexander - Reinstate Monica

6

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

สิ่งนี้นำไปสู่การยึดมั่นที่ดีขึ้นของหลักการความรับผิดชอบเดียว


นี่ควรเป็นคำตอบที่ยอมรับได้
Jack Aidley

0

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

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

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



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