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


9

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

เพื่อสาธิต:

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

module_aครั้งแรกที่ผมเขียน ตอนนี้ฉันต้องการเขียนแบบทดสอบ _private_funcผมอยากจะทดสอบฟังก์ชั่น 'ส่วนตัว' ฉันไม่เข้าใจว่าทำไมฉันถึงไม่เขียนการทดสอบถ้าในภายหลังฉันอาจปรับโครงสร้างให้กับโมดูลภายในของตัวเองแล้วเขียนการทดสอบ


สมมติว่าฉันมีโมดูลที่มีฟังก์ชั่นต่อไปนี้ (มันอาจจะเป็นคลาส):

def public_func(a):
    b = _do_stuff(a)
    return _do_more_stuff(b)

_do_stuffและ_do_more_stuffเป็นฟังก์ชั่น 'ส่วนตัว' ของโมดูล

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

_do_stuffและ_do_more_stuffมีฟังก์ชั่นส่วนใหญ่ของโมดูล แต่ละคนอาจเป็นฟังก์ชั่นสาธารณะของโมดูล 'ภายใน' ที่แตกต่างกัน แต่พวกเขายังไม่พัฒนาและมีขนาดใหญ่พอที่จะแยกไปยังไฟล์แยก

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



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

3
"ฉันควรทดสอบฟังก์ชั่นส่วนตัวหรือไม่" ไม่ไม่เคยเคย
David Arno

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

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

คำตอบ:


14

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

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

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

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

หากการทดสอบผ่านอินเทอร์เฟซสาธารณะไม่ใช้งานฟังก์ชั่นส่วนตัวอย่างเพียงพอฟังก์ชั่นส่วนตัวก็พยายามอนุญาตให้ใช้งานได้มาก

การตรวจสอบสามารถช่วย จำกัด สิ่งที่ฟังก์ชันส่วนตัวอนุญาต หากคุณไม่สามารถผ่านโมฆะผ่านอินเทอร์เฟซสาธารณะคุณยังคงสามารถโยนข้อยกเว้นหากผ่านมา

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

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

นั่นอาจไม่ใช่การทดสอบที่คุณนึก แต่หวังว่าคุณจะเต็มใจที่จะสร้างฟังก์ชั่นตัวช่วยส่วนตัวตามความเหมาะสม

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

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


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

ฉันจะบอกว่าฉันถามในสิ่งเดียวกันกับคนอื่นที่ตอบที่นี่ :) ฉันต้องการฟังความคิดเห็นของทุกคน
Aviv Cohn

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

1
Upvoted สำหรับ "ถ้าการทดสอบผ่านส่วนต่อประสานสาธารณะไม่ได้ใช้งานฟังก์ชั่นส่วนตัวอย่างเพียงพอฟังก์ชั่นส่วนตัวพยายามที่จะอนุญาตให้ใช้มาก"
Kris Welsh

7

คำตอบสั้น ๆ : ไม่

คำตอบอีกต่อไป: ใช่ แต่ผ่าน 'API' สาธารณะของชั้นเรียนของคุณ

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

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


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


สวัสดีขอบคุณสำหรับคำตอบของคุณ :) โปรดอ่านคำถามอีกครั้งฉันได้แก้ไขเพื่อชี้แจง
Aviv Cohn

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

ฉันจะบอกว่าฉันถามในสิ่งเดียวกันกับคนอื่นที่ตอบที่นี่ :) ฉันต้องการฟังความคิดเห็นของทุกคน
Aviv Cohn

5

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

def public_func(a):
    b = _do_stuff(a)
    return _do_more_stuff(b)

หากคุณมีชุดการทดสอบที่ใช้เพียงอย่างเดียวpublic_funcถ้าคุณเขียนมันซ้ำไปที่:

def public_func(a):
    b = _do_all_the_new_stuff(a)
    return _do_all_the_old_stuff(b)

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

นี่คือทุกสิ่งที่ดี: API สาธารณะคงที่; ผลงานภายในห่อหุ้มอย่างดี และการทดสอบที่แข็งแกร่ง

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

นี่คือสิ่งที่ไม่ดี: API ที่เปลี่ยนแปลง เปิดเผยผลงานภายในควบคู่ไปกับการทดสอบอย่างแน่นหนา และการทดสอบที่เปราะบางซึ่งเปลี่ยนแปลงทันทีที่มีการเปลี่ยนแปลงการปฏิบัติ

ดังนั้นไม่อย่าทดสอบฟังก์ชั่นส่วนตัว เคย


สวัสดีเดวิดขอบคุณสำหรับคำตอบของคุณ โปรดอ่านคำถามของฉันอีกครั้งฉันได้แก้ไขเพื่อชี้แจง
Aviv Cohn

Meh ไม่มีอะไรผิดปกติกับการทดสอบฟังก์ชั่นภายใน โดยส่วนตัวฉันไม่ได้เขียนฟังก์ชั่นที่ไม่น่าสนใจใด ๆ เว้นแต่ว่าฉันจะสามารถครอบคลุมมันได้ด้วยการทดสอบหน่วยสองครั้งดังนั้นการห้ามการทดสอบในฟังก์ชั่นภายในจะทำให้ฉันไม่สามารถเขียนฟังก์ชั่นภายในได้ API สามารถและเปลี่ยนแปลงได้ เมื่อทำคุณต้องเปลี่ยนการทดสอบ การทำซ้ำฟังก์ชันภายใน (และการทดสอบ) จะไม่ทำลายการทดสอบฟังก์ชั่นด้านนอก นั่นคือจุดรวมของการทดสอบเหล่านั้น คำแนะนำโดยรวมแย่
Robert Harvey

สิ่งที่คุณโต้เถียงจริงๆก็คือคุณไม่ควรมีฟังก์ชั่นส่วนตัว
Robert Harvey

1
@AvivCohn พวกเขาใหญ่พอที่จะรับประกันการทดสอบซึ่งในกรณีนี้พวกเขาใหญ่พอที่จะรับไฟล์ของตนเอง หรือมีขนาดเล็กพอที่คุณไม่จำเป็นต้องทดสอบแยกต่างหาก แล้วมันคืออะไร?
Doval

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