ฉันควรหลีกเลี่ยงวิธีการส่วนตัวหรือไม่ถ้าฉันใช้ TDD


99

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

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

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

นอกจากนี้ยังถือว่าเป็นการปฏิบัติที่ไม่ดีในการเพิ่มวิธีการเพื่อการทดสอบ

นี่หมายความว่า TDD ขัดแย้งกับการห่อหุ้มหรือไม่? ความสมดุลที่เหมาะสมคืออะไร? ฉันอยากจะเปิดเผยวิธีส่วนใหญ่หรือทั้งหมดของฉันต่อสาธารณะในขณะนี้ ...


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

10
"วิธีการส่วนตัวไม่สามารถทดสอบได้"? ภาษาไหน? ในบางภาษามันไม่สะดวก ในภาษาอื่นมันง่ายอย่างสมบูรณ์แบบ นอกจากนี้คุณบอกว่าหลักการออกแบบของ encapsulation ต้องเสมอจะดำเนินการที่มีจำนวนมากวิธีเอกชน? ดูเหมือนว่าจะสุดขั้วสักหน่อย บางภาษาไม่มีวิธีส่วนตัว แต่ก็ดูเหมือนว่าจะมีการออกแบบที่ห่อหุ้มอย่างดี
S.Lott

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


ดังนั้น @gnat คุณคิดว่าควรปิดคำถามนี้ซ้ำกับคำถามที่เกิดขึ้นจากคำตอบของคำถามนี้หรือไม่? * 8 ')
มาร์กบูธ

คำตอบ:


50

ต้องการทดสอบอินเตอร์เฟสมากกว่าการทดสอบการนำไปใช้

ฉันเข้าใจว่าวิธีการส่วนตัวไม่สามารถทดสอบได้

ขึ้นอยู่กับสภาพแวดล้อมการพัฒนาของคุณดูด้านล่าง

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

ถูกต้องแล้ว TDD มุ่งเน้นไปที่การทดสอบอินเตอร์เฟส

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

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

แม้ว่าระดับไม่มีวิธีการสาธารณะก็จัดการเหตุการณ์ที่มีมันเป็นอินเตอร์เฟซที่สาธารณะและต่อต้านว่า อินเตอร์เฟซที่สาธารณะที่คุณสามารถทดสอบ

เนื่องจากเหตุการณ์เป็นส่วนต่อประสานจึงเป็นเหตุการณ์ที่คุณจะต้องสร้างเพื่อทดสอบวัตถุนั้น

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

นอกจากนี้ยังถือว่าเป็นการปฏิบัติที่ไม่ดีในการเพิ่มวิธีการเพื่อการทดสอบ

แน่นอนคุณควรระมัดระวังในการเปิดเผยสถานะภายใน

นี่หมายความว่า TDD ขัดแย้งกับการห่อหุ้มหรือไม่? ความสมดุลที่เหมาะสมคืออะไร?

ไม่ได้อย่างแน่นอน.

TDD ไม่ควรเปลี่ยนการใช้คลาสของคุณนอกเหนือจากเพื่อทำให้ง่ายขึ้น (โดยการใช้YAGNIจากจุดก่อนหน้า)

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

ฉันอยากจะเปิดเผยวิธีส่วนใหญ่หรือทั้งหมดของฉันต่อสาธารณะในขณะนี้ ...

นี่คงเป็นการโยนทารกออกไปด้วยน้ำอาบ

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

ดูรายละเอียดเพิ่มเติมเกี่ยวกับการทดสอบวิธีการส่วนตัว

หากคุณต้องทดสอบหน่วยพฤติกรรมบางอย่างของชั้นเรียนโดยขึ้นอยู่กับภาษา / สภาพแวดล้อมคุณอาจมีสามตัวเลือก:

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

เห็นได้ชัดว่าตัวเลือกที่ 3 นั้นดีที่สุด

1) ใส่การทดสอบในชั้นเรียนที่คุณต้องการทดสอบ (ไม่เหมาะ)

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

2) เปิดเผยวิธีส่วนตัวที่คุณต้องการทดสอบเป็นวิธีสาธารณะ (ไม่ใช่ความคิดที่ดีจริงๆ)

ดังที่แนะนำว่านี่เป็นวิธีปฏิบัติที่แย่มาก ๆ ทำลายการห่อหุ้มและจะทำให้เกิดการใช้งานภายในกับผู้ใช้รหัส

3) ใช้สภาพแวดล้อมการทดสอบที่ดีขึ้น (ตัวเลือกที่ดีที่สุดถ้ามี)

ในโลกคราส 3. สามารถทำได้โดยการใช้ชิ้นส่วน ในโลก C # เราอาจใช้การเรียนบางส่วน ภาษาอื่น ๆ / สภาพแวดล้อมมักจะมีฟังก์ชั่นที่คล้ายกันคุณเพียงแค่ต้องค้นหามัน

สมมติว่าการสุ่มสี่สุ่มห้า 1. หรือ 2 เป็นทางเลือกเพียงอย่างเดียวที่น่าจะส่งผลให้ซอฟต์แวร์การผลิตป่องด้วยรหัสการทดสอบหรืออินเทอร์เฟซที่น่ารังเกียจในชั้นเรียนที่ล้างผ้าลินินสกปรกในที่สาธารณะ * 8)

  • ทั้งหมดในทุก - มันจะดีกว่ามากที่จะไม่ทดสอบกับการใช้งานส่วนตัวแม้ว่า

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

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

สิ่งที่เกี่ยวกับการทำให้วิธีการเริ่มต้น / ป้องกันและสร้างการทดสอบในโครงการทดสอบด้วยแพคเกจเดียวกัน?
RichardCypher

@RichardCypher นั้นมีประสิทธิภาพเหมือนกับ 2) เนื่องจากคุณกำลังเปลี่ยนแปลงสิ่งที่ specs ของคุณต้องการที่จะรองรับข้อบกพร่องในสภาพแวดล้อมการทดสอบของคุณ
Mark Booth

75

แน่นอนคุณสามารถมีวิธีส่วนตัวและแน่นอนคุณสามารถทดสอบได้

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

ในตัวอย่างของคุณ:

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

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

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

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

ดังนั้นโดยปกติแล้ววิธีการส่วนตัวจะปรากฏเฉพาะเมื่อสกัดจากวิธีสาธารณะที่ผ่านการทดสอบแล้วและผ่านการทดสอบแล้วเช่นกัน


3
การทดสอบด้วยวิธีสาธารณะนั้นใช้งานได้ดี 99% ของเวลา ความท้าทายคือ 1% ของเวลาที่วิธีสาธารณะแบบเดี่ยวของคุณมีโค้ดที่ซับซ้อนหลายร้อยหรือพันบรรทัดด้านหลังและสถานะขั้นกลางทั้งหมดนั้นมีการใช้งานที่เฉพาะเจาะจง เมื่อมันซับซ้อนพอที่จะพยายามตีทุกกรณีจากวิธีสาธารณะกลายเป็นเรื่องเจ็บปวดที่สุด อีกวิธีหนึ่งคือการทดสอบขอบเคสโดยการทำลาย encapsulation และเปิดเผยวิธีการเพิ่มเติมในแบบส่วนตัวหรือโดยใช้ kludge เพื่อให้การทดสอบเรียกเมธอดไพรเวตส่งผลโดยตรงในกรณีทดสอบที่เปราะบางนอกเหนือจากความน่าเกลียด
Dan Neely

24
วิธีการส่วนตัวที่ซับซ้อนขนาดใหญ่นั้นเป็นกลิ่นรหัส การนำไปใช้งานซึ่งมีความซับซ้อนมากจนไม่สามารถย่อยสลายได้อย่างมีประโยชน์ในชิ้นส่วนส่วนประกอบ (ด้วยส่วนต่อประสานสาธารณะ) เป็นปัญหาในการทดสอบที่แสดงให้เห็นถึงการออกแบบที่มีศักยภาพและปัญหาสถาปัตยกรรม 1% ของกรณีที่รหัสส่วนตัวมีขนาดใหญ่มากมักจะได้รับประโยชน์จากการทำงานซ้ำเพื่อย่อยสลายและเปิดเผย
S.Lott

13
@ รหัสแดนนีแบบนี้ไม่สามารถทดสอบได้เลยโดยไม่คำนึงว่า - และส่วนหนึ่งของการเขียนการทดสอบหน่วยกำลังชี้ถึงสิ่งนี้ กำจัดสถานะแบ่งคลาสใช้การรีแฟคเตอร์ทั่วไปทั้งหมดแล้วเขียนการทดสอบหน่วย เช่นเดียวกันกับ TDD คุณถึงจุดนั้นได้อย่างไร นี่เป็นหนึ่งในข้อดีของ TDD การเขียนรหัสทดสอบได้กลายเป็นอัตโนมัติ
Bill K

อย่างน้อยinternalวิธีการหรือวิธีสาธารณะในinternalชั้นเรียนควรทดสอบโดยตรงบ่อยครั้งมาก โชคดีที่. net รองรับInternalsVisibleToAttributeแต่ไม่ได้ทดสอบวิธีการเหล่านั้นจะเป็น PITA
CodesInChaos

25

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

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

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

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


4
+1 คุณสมบัติที่สำคัญอย่างหนึ่งของ TDD คือมันบังคับให้คุณทดสอบว่าสิ่งที่ต้องการนั้นเป็นจริงมากกว่าการทดสอบว่าวิธีการทำสิ่งที่พวกเขาคิดว่าคุณทำ ดังนั้นคำถาม "ฉันสามารถทดสอบวิธีการส่วนตัว" ได้ตรงกันข้ามกับจิตวิญญาณของ TDD - คำถามอาจเป็น "ฉันสามารถทดสอบข้อกำหนดที่มีการใช้งานรวมถึงวิธีการส่วนตัว" และคำตอบสำหรับคำถามนี้ชัดเจนว่าใช่
Dawood ibn Kareem

6

ฉันเข้าใจว่าวิธีการส่วนตัวไม่สามารถทดสอบได้

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

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

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


5

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

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


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

5

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

สิ่งเหล่านี้คือสิ่งที่ฉันจำไว้เพื่อพยายามสร้างสมดุลที่สามารถทำงานได้

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

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


4

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

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

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


4

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


3

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


... สมมติว่านี่คือ Java
Dawood ibn Kareem

หรือinternalใน. net
CodesInChaos

2

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

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


2

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

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

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

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


2

หลีกเลี่ยงการใช้? เลขที่
หลีกเลี่ยงการเริ่มต้นด้วย ? ใช่.

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

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

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