ฉันได้อ่านโพสต์นี้เกี่ยวกับวิธีทดสอบวิธีส่วนตัว ฉันมักจะไม่ทดสอบพวกเขาเพราะฉันมักจะคิดว่ามันเร็วกว่าที่จะทดสอบเฉพาะวิธีสาธารณะที่จะถูกเรียกจากนอกวัตถุ คุณทดสอบวิธีการส่วนตัวหรือไม่? ฉันควรทดสอบพวกเขาเสมอ
ฉันได้อ่านโพสต์นี้เกี่ยวกับวิธีทดสอบวิธีส่วนตัว ฉันมักจะไม่ทดสอบพวกเขาเพราะฉันมักจะคิดว่ามันเร็วกว่าที่จะทดสอบเฉพาะวิธีสาธารณะที่จะถูกเรียกจากนอกวัตถุ คุณทดสอบวิธีการส่วนตัวหรือไม่? ฉันควรทดสอบพวกเขาเสมอ
คำตอบ:
ฉันไม่ได้ทำการทดสอบวิธีการส่วนตัว วิธีการส่วนตัวคือรายละเอียดการใช้งานที่ควรซ่อนอยู่กับผู้ใช้ของชั้นเรียน การทดสอบวิธีการส่วนตัวจะทำลายการห่อหุ้ม
หากฉันพบว่าวิธีการส่วนตัวนั้นมีขนาดใหญ่หรือซับซ้อนหรือมีความสำคัญพอที่จะต้องมีการทดสอบของตัวเองฉันก็แค่วางไว้ในคลาสอื่นและทำให้เป็นแบบสาธารณะ ( Object Object ) จากนั้นฉันสามารถทดสอบวิธีก่อนหน้านี้ส่วนตัว แต่ตอนนี้สาธารณะได้อย่างง่ายดายซึ่งตอนนี้อาศัยอยู่ในชั้นเรียนของตัวเอง
วัตถุประสงค์ของการทดสอบคืออะไร?
คำตอบส่วนใหญ่จนถึงขณะนี้กำลังบอกว่าวิธีการแบบส่วนตัวนั้นมีรายละเอียดการใช้งานที่ไม่สำคัญ (หรืออย่างน้อยก็ไม่ควร) ตราบใดที่ส่วนต่อประสานสาธารณะนั้นผ่านการทดสอบและใช้งานได้ดี ถูกต้องอย่างแน่นอนหากจุดประสงค์ในการทดสอบของคุณคือการรับประกันว่าส่วนต่อประสานสาธารณะนั้นใช้งานได้
โดยส่วนตัวแล้วการใช้งานหลักของฉันสำหรับการทดสอบรหัสคือเพื่อให้แน่ใจว่าการเปลี่ยนแปลงรหัสในอนาคตจะไม่ทำให้เกิดปัญหาและเพื่อช่วยในการแก้ไขข้อบกพร่องหากมี ฉันพบว่าการทดสอบวิธีส่วนตัวเช่นเดียวกับส่วนต่อประสานสาธารณะ (ถ้าไม่มากไปกว่านั้น!) ยิ่งทำให้จุดประสงค์นั้นมากขึ้น
ลองพิจารณา: คุณมีวิธีสาธารณะ A ซึ่งเรียกใช้วิธีส่วนตัว B. A และ B ทั้งสองวิธีใช้วิธี C C. เปลี่ยนแปลง (อาจเป็นโดยคุณอาจเป็นผู้ขาย) ทำให้ A เริ่มล้มเหลวในการทดสอบ มันจะไม่มีประโยชน์ที่จะมีการทดสอบสำหรับ B เช่นกันแม้ว่าจะเป็นแบบส่วนตัวเพื่อให้คุณรู้ว่าปัญหาคือการใช้ C, การใช้ C ของ B หรือทั้งสองอย่าง?
การทดสอบวิธีการส่วนตัวยังเพิ่มมูลค่าในกรณีที่การทดสอบครอบคลุมของส่วนต่อประสานสาธารณะไม่สมบูรณ์ ในขณะนี้เป็นสถานการณ์ที่เราต้องการหลีกเลี่ยงโดยทั่วไปการทดสอบหน่วยประสิทธิภาพขึ้นอยู่กับการทดสอบการค้นหาข้อบกพร่องและค่าใช้จ่ายในการพัฒนาและบำรุงรักษาที่เกี่ยวข้องของการทดสอบเหล่านั้น ในบางกรณีประโยชน์ของการครอบคลุมการทดสอบ 100% อาจถูกตัดสินว่าไม่เพียงพอที่จะรับประกันค่าใช้จ่ายของการทดสอบเหล่านั้นซึ่งจะทำให้เกิดช่องว่างในการครอบคลุมการทดสอบของส่วนต่อประสานสาธารณะ ในกรณีเช่นนี้การทดสอบที่กำหนดเป้าหมายเป็นอย่างดีของวิธีส่วนตัวอาจเป็นการเพิ่มประสิทธิภาพให้กับฐานรหัสได้อย่างมาก
testDoSomething()
testDoSomethingPrivate()
สิ่งนี้ทำให้การทดสอบมีค่าน้อยลง . ต่อไปนี้เป็นเหตุผลเพิ่มเติมสำหรับการทดสอบstackoverflow.com/questions/34571/ส่วนบุคคลที่เป็นส่วนตัว :
ฉันมักจะทำตามคำแนะนำของ Dave Thomas และ Andy Hunt ในหนังสือการทดสอบ Pragmatic Unitของพวกเขา:
โดยทั่วไปคุณไม่ต้องการทำลาย encapsulation ใด ๆ เพื่อประโยชน์ในการทดสอบ (หรือตามที่แม่เคยบอกว่า "อย่าเปิดเผยสิทธิ์ของคุณ!") ส่วนใหญ่แล้วคุณควรจะทดสอบคลาสด้วยการใช้วิธีการสาธารณะ หากมีฟังก์ชั่นการใช้งานที่สำคัญซึ่งซ่อนอยู่หลังการเข้าถึงแบบส่วนตัวหรือแบบป้องกันนั่นอาจเป็นสัญญาณเตือนว่ามีอีกชั้นหนึ่งที่ต้องดิ้นรนออกไป
แต่บางครั้งฉันไม่สามารถหยุดตัวเองจากการทดสอบวิธีการส่วนตัวเพราะมันทำให้ฉันรู้สึกมั่นใจว่าฉันกำลังสร้างโปรแกรมที่แข็งแกร่งอย่างสมบูรณ์
ฉันรู้สึกถูกบังคับให้ต้องทดสอบฟังก์ชั่นส่วนตัวเมื่อฉันทำตามคำแนะนำ QA ล่าสุดของเรามากขึ้นเรื่อย ๆ ในโครงการของเรา:
ไม่เกิน 10 ในความซับซ้อนของวงจรต่อฟังก์ชัน
ตอนนี้ผลข้างเคียงของการบังคับใช้นโยบายนี้คือการที่ฟังก์ชั่นสาธารณะที่มีขนาดใหญ่มากของฉันได้รับการแบ่งออกเป็นฟังก์ชั่นส่วนตัวที่มุ่งเน้นและมีชื่อมากกว่า
ฟังก์ชั่นสาธารณะยังคงมีอยู่ (แน่นอน) แต่จะลดลงอย่างมากที่จะเรียกว่า 'ฟังก์ชั่นย่อย' ส่วนตัวเหล่านั้นทั้งหมด
นั่นคือเจ๋งจริงเพราะ callstack ตอนนี้อ่านง่ายกว่ามาก (แทนที่จะเป็นบั๊กในฟังก์ชั่นใหญ่ฉันมีข้อผิดพลาดในฟังก์ชั่นย่อยที่มีชื่อฟังก์ชั่นก่อนหน้าใน callstack เพื่อช่วยให้ฉันเข้าใจ 'ฉันไปถึงที่นั่นได้อย่างไร')
อย่างไรก็ตามในตอนนี้ดูเหมือนง่ายต่อการทดสอบหน่วยโดยตรงกับฟังก์ชั่นส่วนตัวเหล่านั้นและปล่อยให้การทดสอบฟังก์ชั่นสาธารณะที่มีขนาดใหญ่เป็นการทดสอบแบบ 'รวม' บางอย่างที่สถานการณ์จำเป็นต้องได้รับการแก้ไข
แค่ 2 เซ็นต์ของฉัน
ใช่ฉันทำการทดสอบฟังก์ชั่นส่วนตัวเพราะถึงแม้ว่ามันจะถูกทดสอบด้วยวิธีสาธารณะของคุณมันดีใน TDD (Test Driven Design) เพื่อทดสอบส่วนที่เล็กที่สุดของแอปพลิเคชัน แต่ฟังก์ชั่นส่วนตัวจะไม่สามารถเข้าถึงได้เมื่อคุณอยู่ในคลาสหน่วยทดสอบ นี่คือสิ่งที่เราทำเพื่อทดสอบวิธีการส่วนตัวของเรา
ทำไมเราถึงมีวิธีส่วนตัว
ฟังก์ชั่นส่วนตัวส่วนใหญ่มีอยู่ในชั้นเรียนของเราเพราะเราต้องการสร้างรหัสที่สามารถอ่านได้ในวิธีการสาธารณะของเรา เราไม่ต้องการให้ผู้ใช้ของชั้นนี้เรียกวิธีการเหล่านี้โดยตรง แต่ผ่านวิธีการสาธารณะของเรา นอกจากนี้เราไม่ต้องการเปลี่ยนพฤติกรรมของพวกเขาเมื่อขยายชั้นเรียน (ในกรณีที่มีการป้องกัน) ดังนั้นจึงเป็นเรื่องส่วนตัว
เมื่อเราใช้รหัสเราใช้การทดสอบที่ขับเคลื่อนด้วยการออกแบบ (TDD) ซึ่งหมายความว่าบางครั้งเราพบชิ้นส่วนของฟังก์ชันที่เป็นส่วนตัวและต้องการทดสอบ ฟังก์ชั่นส่วนตัวไม่สามารถทดสอบได้ใน phpUnit เพราะเราไม่สามารถเข้าถึงได้ในคลาสทดสอบ (เป็นแบบส่วนตัว)
เราคิดว่าที่นี่มี 3 โซลูชั่น:
1. คุณสามารถทดสอบสิทธิ์ของคุณผ่านวิธีสาธารณะของคุณ
ข้อดี
ข้อเสีย
2. ถ้าความเป็นส่วนตัวมีความสำคัญมากบางทีมันอาจเป็นรหัสสำหรับสร้างคลาสแยกใหม่สำหรับมัน
ข้อดี
ข้อเสีย
3. เปลี่ยนโมดิฟายเออร์สำหรับการเข้าถึงเป็นที่ป้องกัน
ข้อดี
ข้อเสีย
ตัวอย่าง
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 เพื่อทดสอบฟังก์ชันส่วนตัวในอดีตของเรา
ฉันไม่ชอบการทดสอบการใช้งานส่วนตัวด้วยเหตุผลสองประการ พวกเขามีดังนี้ (เหล่านี้เป็นประเด็นหลักสำหรับคน TLDR):
ฉันจะอธิบายแต่ละข้อด้วยตัวอย่างที่เป็นรูปธรรม ปรากฎว่า 2) และ 3) มีการเชื่อมต่อที่ค่อนข้างซับซ้อนดังนั้นตัวอย่างของพวกเขาก็คล้ายกันแม้ว่าฉันจะพิจารณาเหตุผลแยกกันว่าทำไมคุณไม่ควรทดสอบวิธีการส่วนตัว
มีหลายครั้งที่การทดสอบวิธีการส่วนตัวมีความเหมาะสมเป็นสิ่งสำคัญที่ควรระวังข้อเสียที่กล่าวข้างต้น ฉันจะไปดูรายละเอียดเพิ่มเติมในภายหลัง
ฉันยังอธิบายด้วยว่าทำไม TDD ไม่ใช่ข้อแก้ตัวที่ถูกต้องสำหรับการทดสอบวิธีการส่วนตัวในตอนท้าย
หนึ่งใน paterns ที่พบบ่อยที่สุดที่ฉันเห็นคือสิ่งที่Michael Feathersเรียกว่า"Iceberg" (ถ้าคุณไม่รู้ว่า Michael Feathers คือใครไปซื้อ / อ่านหนังสือของเขา "การทำงานอย่างมีประสิทธิภาพด้วยรหัสมรดก" เขาเป็น บุคคลที่ควรค่าแก่การรู้หากคุณเป็นวิศวกรซอฟต์แวร์ / นักพัฒนาซอฟต์แวร์มืออาชีพ) ยังมีรูปแบบอื่น ๆ (ต่อต้าน) ที่ทำให้เกิดปัญหานี้ขึ้น แต่นี่เป็นรูปแบบที่พบบ่อยที่สุดที่ฉันเคยเจอ คลาส "Iceberg" มีวิธีสาธารณะหนึ่งวิธีและส่วนที่เหลือเป็นแบบส่วนตัว มันเรียกว่าคลาส "ภูเขาน้ำแข็ง" เพราะโดยปกติจะมีวิธีการสาธารณะที่โดดเดี่ยวโผล่ขึ้นมา แต่ส่วนที่เหลือของฟังก์ชั่นจะถูกซ่อนอยู่ใต้น้ำในรูปแบบของวิธีการส่วนตัว
ตัวอย่างเช่นคุณอาจต้องการทดสอบ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()
RuleEvaluator
Tokenizer
วัตถุในฐานะสมาชิก ตอนนี้เราสามารถทำการทดสอบแบบเดียวกันกับข้างต้นได้ยกเว้นว่าเราจะทำการทดสอบTokenizer
คลาสแทนRuleEvaluator
คลาส
นี่คือสิ่งที่ดูเหมือนใน UML:
โปรดทราบว่าการออกแบบใหม่นี้เพิ่มความเป็นโมดูลดังนั้นคุณสามารถใช้คลาสเหล่านี้ในส่วนอื่น ๆ ของระบบของคุณ (ก่อนที่คุณจะทำไม่ได้เมธอดส่วนตัวไม่สามารถนำมาใช้ใหม่ได้ตามคำจำกัดความ) นี่เป็นข้อได้เปรียบหลักของการทำลาย 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
คลาสเพื่อจัดการงาน โดยธรรมชาติแล้วคุณจะคาดหวังว่าการทดสอบหนึ่งครั้งจะพัง แต่ความเจ็บปวดนั้นจะเพิ่มขึ้นเมื่อคุณทดสอบการทำงานส่วนตัว
ไม่มี "หนึ่งขนาดเหมาะกับทุกคน" ในซอฟต์แวร์ บางครั้งมันก็โอเค (และเหมาะอย่างยิ่ง) ในการ "ทำลายกฎ" ฉันขอแนะนำว่าอย่าทดสอบการทำงานส่วนตัวเมื่อคุณสามารถ มีสองสถานการณ์หลักเมื่อฉันคิดว่าไม่เป็นไร:
ฉันทำงานกับระบบดั้งเดิมอย่างกว้างขวาง (ซึ่งเป็นเหตุผลว่าทำไมฉันถึงเป็นแฟนตัวยงของ Michael Feathers) และฉันสามารถพูดได้อย่างปลอดภัยว่าบางครั้งมันก็ปลอดภัยที่สุดที่จะทดสอบการใช้งานส่วนตัว มันจะมีประโยชน์โดยเฉพาะอย่างยิ่งสำหรับการ "ทดสอบคุณสมบัติ" ลงในพื้นฐาน
คุณกำลังรีบและต้องทำสิ่งที่เร็วที่สุดที่เป็นไปได้สำหรับที่นี่และตอนนี้ ในระยะยาวคุณไม่ต้องการทดสอบวิธีการส่วนตัว แต่ฉันจะบอกว่ามันมักจะใช้เวลาในการ refactor เพื่อแก้ไขปัญหาการออกแบบ และบางครั้งคุณต้องจัดส่งในหนึ่งสัปดาห์ ไม่เป็นไร: ทำเร็วและสกปรกและทดสอบวิธีส่วนตัวโดยใช้เครื่องมือการคล้าหาถ้านั่นคือสิ่งที่คุณคิดว่าเป็นวิธีที่เร็วและเชื่อถือได้มากที่สุดในการทำงานให้เสร็จ แต่เข้าใจว่าสิ่งที่คุณทำนั้นไม่ดีในระยะยาวและโปรดกลับมาพิจารณาอีกครั้ง (หรือถ้ามันถูกลืม แต่คุณจะเห็นในภายหลังให้แก้ไข)
อาจมีสถานการณ์อื่น ๆ ที่ไม่เป็นไร หากคุณคิดว่าไม่เป็นไรและคุณมีเหตุผลที่ดีแล้วก็ทำได้ ไม่มีใครหยุดคุณได้ เพียงตระหนักถึงต้นทุนที่อาจเกิดขึ้น
นอกจากนี้ฉันไม่ชอบคนที่ใช้ TDD เป็นข้อแก้ตัวสำหรับการทดสอบวิธีการส่วนตัว ฉันฝึกฝน TDD และฉันไม่คิดว่า TDD บังคับให้คุณทำสิ่งนี้ คุณสามารถเขียนการทดสอบของคุณ (สำหรับส่วนต่อประสานสาธารณะของคุณ) ก่อนจากนั้นจึงเขียนรหัสเพื่อตอบสนองส่วนต่อประสานนั้น บางครั้งฉันเขียนการทดสอบสำหรับส่วนต่อประสานสาธารณะและฉันจะตอบสนองด้วยการเขียนวิธีส่วนตัวเล็ก ๆ หนึ่งหรือสองวิธี (แต่ฉันไม่ได้ทดสอบวิธีส่วนตัวโดยตรง แต่ฉันรู้ว่าพวกเขาทำงานหรือการทดสอบสาธารณะของฉันจะล้มเหลว ) ถ้าฉันต้องการทดสอบเคสแบบขอบของวิธีการแบบส่วนตัวนั้นฉันจะเขียนการทดสอบทั้งหมดที่จะกระทบพวกเขาผ่านทางส่วนต่อประสานสาธารณะของฉันหากคุณไม่สามารถหาวิธีการตีเคสขอบได้นี่เป็นสัญญาณบ่งบอกว่าคุณต้องปรับโครงสร้างชิ้นส่วนเล็ก ๆ ด้วยวิธีสาธารณะของตัวเอง มันเป็นสัญญาณที่คุณฟังก์ชั่นส่วนตัวกำลังจะทำมากเกินไปและอยู่นอกขอบเขตของการเรียน
นอกจากนี้บางครั้งฉันก็พบว่าฉันเขียนการทดสอบที่ใหญ่เกินกว่าจะกัดได้ในขณะนี้ดังนั้นฉันจึงคิดว่า "เอ๊ะฉันจะกลับมาทดสอบอีกครั้งในภายหลังเมื่อฉันมี API ทำงานมากกว่า" (ฉัน ฉันจะแสดงความคิดเห็นและเก็บไว้ในใจของฉัน) นี่คือที่ที่ devs จำนวนมากที่ฉันได้พบจะเริ่มเขียนการทดสอบสำหรับการใช้งานส่วนตัวโดยใช้ TDD เป็นแพะรับบาป พวกเขาพูดว่า "โอ้ฉันต้องการการทดสอบอื่น แต่เพื่อที่จะเขียนการทดสอบนั้นฉันจะต้องใช้วิธีการส่วนตัวเหล่านี้ดังนั้นเนื่องจากฉันไม่สามารถเขียนรหัสการผลิตใด ๆ โดยไม่ต้องเขียนการทดสอบฉันต้องเขียนแบบทดสอบ สำหรับวิธีส่วนตัว " แต่สิ่งที่พวกเขาต้องทำจริงๆก็คือการเปลี่ยนโครงสร้างให้เป็นส่วนประกอบที่เล็กลงและนำกลับมาใช้ซ้ำได้แทนที่จะเพิ่ม / ทดสอบวิธีการส่วนตัวในชั้นเรียนปัจจุบัน
บันทึก:
ฉันตอบคำถามที่คล้ายกันเกี่ยวกับการทดสอบวิธีการส่วนตัวโดยใช้ GoogleTestสักครู่แล้ว ฉันได้แก้ไขคำตอบนั้นเป็นภาษาที่ไม่เชื่อเรื่องพระเจ้ามากขึ้นที่นี่
ป.ล. นี่คือการบรรยายที่เกี่ยวข้องเกี่ยวกับคลาสภูเขาน้ำแข็งและเครื่องมือการคล้าหาโดย Michael Feathers: https://www.youtube.com/watch?v=4cVZvoFGJTU
_
ก็จะส่งสัญญาณ "เฮ้นี่คือ" ส่วนตัว "คุณสามารถใช้งานได้ แต่การเปิดเผยแบบเต็มมันไม่ได้ถูกออกแบบมาเพื่อนำมาใช้ซ้ำและคุณควรใช้เฉพาะในกรณีที่คุณจริงๆรู้ว่าคุณกำลังทำอะไรอยู่ ". คุณอาจจะใช้วิธีการเดียวกันในภาษาใด ๆ : ทำให้บรรดาสมาชิกที่สาธารณะ _
แต่พวกเขาทำกับชั้นนำ หรือบางทีฟังก์ชั่นเหล่านั้นควรเป็นแบบส่วนตัวและเพิ่งทดสอบผ่านส่วนต่อประสานสาธารณะ (ดูคำตอบสำหรับรายละเอียดเพิ่มเติม) เป็นกรณี ๆ ไปไม่มีกฎทั่วไป
ฉันคิดว่าเป็นการดีที่สุดที่จะทดสอบอินเทอร์เฟซสาธารณะของวัตถุ จากมุมมองของโลกภายนอกมีเพียงพฤติกรรมของส่วนต่อประสานสาธารณะเท่านั้นและนี่คือสิ่งที่หน่วยทดสอบของคุณควรมุ่งไป
เมื่อคุณมีการทดสอบหน่วยของแข็งสำหรับวัตถุคุณไม่ต้องการกลับไปและเปลี่ยนการทดสอบเหล่านั้นเพียงเพราะการใช้งานที่อยู่เบื้องหลังส่วนต่อประสานเปลี่ยนไป ในสถานการณ์นี้คุณได้ทำลายความสอดคล้องของการทดสอบหน่วยของคุณ
หากวิธีการส่วนตัวของคุณไม่ได้รับการทดสอบโดยการโทรวิธีสาธารณะของคุณแล้วมันทำอะไร? ฉันกำลังพูดคุยส่วนตัวไม่ได้รับการป้องกันหรือเพื่อน
หากวิธีการส่วนตัวถูกกำหนดไว้อย่างดี (เช่นมันมีฟังก์ชั่นที่สามารถทดสอบได้และไม่ได้หมายถึงการเปลี่ยนแปลงตลอดเวลา) ใช่แล้ว ฉันทดสอบทุกอย่างที่สามารถทดสอบได้ในที่ที่เหมาะสม
ตัวอย่างเช่นไลบรารีการเข้ารหัสอาจซ่อนความจริงที่ว่ามันทำการบล็อกการเข้ารหัสด้วยวิธีส่วนตัวที่เข้ารหัสเพียง 8 ไบต์ต่อครั้ง ฉันจะเขียนการทดสอบหน่วยสำหรับเรื่องนั้น - มันไม่ได้หมายถึงการเปลี่ยนแปลงแม้ว่ามันจะถูกซ่อนอยู่และถ้ามันหยุด (เนื่องจากการปรับปรุงประสิทธิภาพในอนาคตเป็นต้น) จากนั้นฉันต้องการที่จะรู้ว่ามันเป็นฟังก์ชั่นส่วนตัวที่พัง ที่หนึ่งในหน้าที่สาธารณะยากจน
มันเร็วแก้จุดบกพร่องในภายหลัง
อดัม
หากคุณกำลังพัฒนาแบบทดสอบขับเคลื่อน (TDD) คุณจะทดสอบวิธีการส่วนตัวของคุณ
ฉันไม่ใช่ผู้เชี่ยวชาญในด้านนี้ แต่การทดสอบหน่วยควรทดสอบพฤติกรรมไม่ใช่การนำไปใช้ วิธีการส่วนตัวเป็นส่วนหนึ่งของการดำเนินการอย่างเคร่งครัดดังนั้นไม่ควรทดสอบ IMHO
เราทดสอบวิธีการแบบส่วนตัวโดยการอนุมานซึ่งฉันหมายถึงเรามองหาความครอบคลุมการทดสอบระดับรวมอย่างน้อย 95% แต่มีเพียงการทดสอบของเราที่เรียกว่าเป็นวิธีสาธารณะหรือภายใน ในการรับความคุ้มครองเราจำเป็นต้องโทรหลายครั้งไปยังสาธารณะ / internals ตามสถานการณ์ต่าง ๆ ที่อาจเกิดขึ้น สิ่งนี้ทำให้การทดสอบของเรามีความตั้งใจมากขึ้นในจุดประสงค์ของรหัสที่ใช้ในการทดสอบ
คำตอบของ Trumpi ต่อโพสต์ที่คุณเชื่อมโยงนั้นเป็นคำตอบที่ดีที่สุด
การทดสอบหน่วยที่ฉันเชื่อว่าใช้สำหรับการทดสอบวิธีสาธารณะ วิธีการสาธารณะของคุณใช้วิธีการส่วนตัวของคุณดังนั้นพวกเขาจึงได้รับการทดสอบทางอ้อม
ฉันเจอปัญหานี้มาระยะหนึ่งแล้วโดยเฉพาะอย่างยิ่งกับการลองใช้มือที่ TDD
ฉันเจอโพสต์สองที่ฉันคิดว่าปัญหานี้อย่างละเอียดพอในกรณีของ TDD
สรุป:
เมื่อใช้เทคนิคการพัฒนาแบบทดสอบขับเคลื่อน (ออกแบบ), วิธีการส่วนตัวควรเกิดขึ้นเฉพาะในระหว่างกระบวนการ re-factoring ของรหัสที่ทำงานแล้วและทดสอบแล้ว
โดยธรรมชาติของกระบวนการบิตใด ๆ ของฟังก์ชั่นการใช้งานที่เรียบง่ายที่ดึงออกมาจากฟังก์ชั่นการทดสอบอย่างละเอียดจะไม่ว่าจะเป็นการทดสอบด้วยตนเอง (เช่นการทดสอบทางอ้อมครอบคลุม)
สำหรับฉันแล้วดูเหมือนชัดเจนพอที่ในตอนเริ่มต้นของการเข้ารหัสวิธีส่วนใหญ่จะเป็นฟังก์ชั่นระดับที่สูงขึ้นเพราะพวกเขากำลังห่อหุ้ม / อธิบายการออกแบบ
ดังนั้นวิธีการเหล่านี้จะเป็นแบบสาธารณะและทดสอบได้ง่ายพอ
วิธีการภาคเอกชนจะมาในภายหลังเมื่อทุกอย่างทำงานได้ดีและเรามีอีกครั้งแฟเพื่อประโยชน์ของการอ่านและการรักษาความสะอาด
ตามที่ยกมาข้างต้น "ถ้าคุณไม่ทดสอบวิธีส่วนตัวของคุณคุณจะรู้ได้อย่างไรว่าพวกเขาจะไม่แตก"
นี่เป็นปัญหาสำคัญ หนึ่งในประเด็นสำคัญของการทดสอบหน่วยคือการรู้ว่าที่ไหนเมื่อไรและอย่างไรที่มีบางสิ่งแตกสลายโดยเร็ว จึงลดจำนวนการพัฒนาและความพยายามในการควบคุมคุณภาพเป็นจำนวนมาก หากทุกสิ่งที่ได้รับการทดสอบนั้นเป็นสาธารณะคุณจะไม่มีความคุ้มครองที่ซื่อสัตย์และการตีความภายในของชั้นเรียน
ฉันได้พบหนึ่งในวิธีที่ดีที่สุดในการทำเช่นนี้เพียงเพิ่มการอ้างอิงการทดสอบในโครงการและวางการทดสอบในชั้นเรียนขนานกับวิธีการส่วนตัว ใส่ตรรกะการสร้างที่เหมาะสมเพื่อให้การทดสอบไม่ได้สร้างในโครงการสุดท้าย
จากนั้นคุณจะได้รับประโยชน์ทั้งหมดจากการทดสอบวิธีการเหล่านี้และคุณสามารถพบปัญหาในไม่กี่วินาทีเทียบกับนาทีหรือชั่วโมง
ดังนั้นโดยสรุปใช่หน่วยทดสอบวิธีส่วนตัวของคุณ
คุณไม่ควร หากวิธีการส่วนตัวของคุณมีความซับซ้อนมากพอที่จะต้องทำการทดสอบคุณควรนำไปใช้กับคลาสอื่น รักษาความเชื่อมโยงกันสูงคลาสควรมีจุดประสงค์เดียวเท่านั้น อินเทอร์เฟซสาธารณะระดับควรเพียงพอ
หากคุณไม่ทดสอบวิธีส่วนตัวของคุณคุณจะรู้ได้อย่างไรว่าพวกเขาจะไม่หยุดพัก
ฉันเข้าใจในมุมมองว่าวิธีการส่วนตัวใดที่ถือว่าเป็นรายละเอียดการใช้งานแล้วไม่จำเป็นต้องทดสอบ และฉันจะยึดถือกฎนี้ถ้าเราต้องพัฒนานอกวัตถุเท่านั้น แต่เราเราเป็นนักพัฒนาที่มีข้อ จำกัด บางอย่างที่กำลังพัฒนานอกวัตถุเรียกว่าวิธีการสาธารณะเท่านั้น หรือว่าเรากำลังพัฒนาวัตถุนั้นจริงหรือ เนื่องจากเราไม่ผูกพันกับโปรแกรมนอกวัตถุเราอาจจะต้องเรียกวิธีการส่วนตัวเหล่านั้นให้เป็นสาธารณะใหม่ที่เรากำลังพัฒนา มันจะไม่ดีที่รู้ว่าวิธีการส่วนตัวต่อต้านต่อราคาทั้งหมด
ฉันรู้ว่าบางคนสามารถตอบว่าถ้าเรากำลังพัฒนาวิธีการสาธารณะอื่นในวัตถุนั้นควรทดสอบนี้และที่มัน (วิธีส่วนตัวสามารถดำเนินชีวิตโดยไม่ต้องทดสอบ) แต่สิ่งนี้ก็เป็นจริงสำหรับวิธีสาธารณะใด ๆ ของวัตถุ: เมื่อพัฒนาเว็บแอปวิธีสาธารณะทั้งหมดของวัตถุจะถูกเรียกจากวิธีการควบคุมและด้วยเหตุนี้จึงถือได้ว่าเป็นรายละเอียดการใช้งานสำหรับตัวควบคุม
แล้วทำไมเราถึงทำการทดสอบวัตถุ เพราะมันยากจริง ๆ ไม่ต้องบอกว่าเป็นไปไม่ได้เพื่อให้แน่ใจว่าเรากำลังทดสอบวิธีการของตัวควบคุมด้วยอินพุตที่เหมาะสมซึ่งจะทำให้เกิดกิ่งก้านทั้งหมดของโค้ดพื้นฐาน กล่าวอีกนัยหนึ่งยิ่งเราอยู่ในกองซ้อนมากเท่าไหร่ก็ยิ่งยากขึ้นเท่านั้นที่จะทดสอบพฤติกรรมทั้งหมด และเช่นเดียวกันสำหรับวิธีการส่วนตัว
สำหรับฉันแล้วพรมแดนระหว่างวิธีการส่วนตัวและสาธารณะเป็นเกณฑ์ด้านจิตวิทยาเมื่อพูดถึงการทดสอบ เกณฑ์ที่สำคัญสำหรับฉันคือ:
หากฉันพบว่าวิธีการส่วนตัวนั้นมีขนาดใหญ่หรือซับซ้อนหรือมีความสำคัญพอที่จะต้องมีการทดสอบของตัวเองฉันก็แค่วางไว้ในคลาสอื่นและทำให้เป็นแบบสาธารณะ (Object Object) จากนั้นฉันสามารถทดสอบส่วนตัวก่อนหน้านี้ได้อย่างง่ายดาย แต่ตอนนี้วิธีการสาธารณะที่ตอนนี้อาศัยอยู่ในชั้นเรียนของตัวเอง
ฉันไม่เคยเข้าใจแนวคิดของการทดสอบหน่วย แต่ตอนนี้ฉันรู้แล้วว่าอะไรคือวัตถุประสงค์
การทดสอบหน่วยไม่ได้เป็นทดสอบที่สมบูรณ์ ดังนั้นจึงไม่ใช่การแทนที่สำหรับ QA และการทดสอบด้วยตนเอง แนวคิดของ TDD ในด้านนี้ผิดเนื่องจากคุณไม่สามารถทดสอบทุกอย่างรวมถึงวิธีการส่วนตัว แต่ยังรวมถึงวิธีการที่ใช้ทรัพยากร (โดยเฉพาะทรัพยากรที่เราไม่สามารถควบคุมได้) TDD กำลังพิจารณาคุณภาพทั้งหมดเป็นสิ่งที่ไม่สามารถทำได้
การทดสอบหน่วยเป็นการทดสอบแบบpivotมากกว่า คุณทำเครื่องหมาย pivot ตามใจชอบและผลลัพธ์ของ pivot ควรเหมือนเดิม
สาธารณะกับส่วนตัวไม่ใช่ความแตกต่างที่มีประโยชน์สำหรับสิ่งที่ apis โทรจากการทดสอบของคุณและเป็นวิธีเทียบกับระดับ หน่วยทดสอบส่วนใหญ่สามารถมองเห็นได้ในบริบทเดียว แต่ซ่อนอยู่ในหน่วยอื่น
สิ่งที่สำคัญคือความครอบคลุมและค่าใช้จ่าย คุณต้องลดค่าใช้จ่ายในขณะที่บรรลุเป้าหมายการครอบคลุมของโครงการของคุณ (บรรทัด, สาขา, เส้นทาง, บล็อก, วิธี, คลาส, คลาสเทียบเท่า, กรณีใช้ ... สิ่งที่ทีมตัดสินใจ)
ดังนั้นการใช้เครื่องมือเพื่อให้ครอบคลุมและการออกแบบการทดสอบของคุณจะก่อให้เกิดค่าใช้จ่ายน้อยที่สุด (ในระยะสั้นและระยะยาว )
อย่าทำการทดสอบแพงกว่าที่จำเป็น ถ้ามันถูกที่สุดที่จะทดสอบจุดเข้าสาธารณะเท่านั้น หากถูกที่สุดในการทดสอบวิธีการส่วนตัวให้ทำเช่นนั้น
เมื่อคุณได้รับประสบการณ์มากขึ้นคุณจะดีขึ้นในการทำนายว่าควรปรับปรุงการใช้งานใหม่เมื่อใดเพื่อหลีกเลี่ยงค่าใช้จ่ายในการบำรุงรักษาทดสอบในระยะยาว
หากวิธีการนั้นมีความสำคัญเพียงพอ / ซับซ้อนเพียงพอฉันมักจะทำให้มัน "ป้องกัน" และทดสอบ วิธีการบางอย่างจะถูกปล่อยให้เป็นส่วนตัวและผ่านการทดสอบโดยปริยายว่าเป็นส่วนหนึ่งของการทดสอบหน่วยสำหรับวิธีการสาธารณะ / การป้องกัน
ฉันเห็นคนจำนวนมากอยู่ในแนวความคิดเดียวกัน: ทดสอบในระดับสาธารณะ แต่นั่นไม่ใช่สิ่งที่ทีม QA ของเราทำ พวกเขาทดสอบอินพุตและเอาต์พุตที่คาดหวัง หากในฐานะนักพัฒนาเราทำการทดสอบวิธีการสาธารณะเท่านั้นเราแค่ทำซ้ำงานของ QA และไม่เพิ่มมูลค่าใด ๆ โดย "การทดสอบหน่วย"
คำตอบของ "ฉันควรทดสอบวิธีการส่วนตัวหรือไม่" คือ "....... บางครั้ง" โดยทั่วไปคุณควรทดสอบกับอินเทอร์เฟซของคลาสของคุณ
นี่คือตัวอย่าง:
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 ของคุณอาจมีปัญหาในการใช้ตรรกะจำนวนมากผ่านมันซ้ำแล้วซ้ำอีก ในกรณีนี้บางทีคุณอาจต้องการแยกออกเป็นคลาสแยกให้อินเทอร์เฟซคลาสนั้นและทดสอบกับคลาสนั้น
และสุดท้ายสมมติว่าวัตถุหลักของคุณมีน้ำหนักมากมากและวิธีการนั้นค่อนข้างเล็กและคุณต้องแน่ใจว่าผลลัพธ์ถูกต้อง คุณกำลังคิดว่า "ฉันต้องทดสอบวิธีการส่วนตัวนี้!" คุณคิดว่าคุณสามารถทำให้วัตถุของคุณมีน้ำหนักเบาลงโดยส่งผ่านงานหนักบางอย่างเป็นพารามิเตอร์เริ่มต้นหรือไม่ จากนั้นคุณสามารถผ่านสิ่งที่มีน้ำหนักเบาและทดสอบกับสิ่งนั้น
ไม่คุณไม่ควรทดสอบวิธีการส่วนตัวทำไม และยิ่งไปกว่านั้นกรอบการเยาะเย้ยที่เป็นที่นิยมเช่น Mockito ไม่ได้ให้การสนับสนุนสำหรับการทดสอบวิธีการส่วนตัว
ประเด็นหลักอย่างหนึ่งก็คือ
หากเราทดสอบเพื่อให้แน่ใจว่าถูกต้องของตรรกะและวิธีส่วนตัวดำเนินการตรรกะเราควรทดสอบ ไม่ใช่เหรอ แล้วเราจะข้ามไปทำไม
การทดสอบการเขียนตามการมองเห็นของวิธีการเป็นความคิดที่ไม่เกี่ยวข้องอย่างสมบูรณ์
โดยตรงกันข้าม
ในทางกลับกันการเรียกวิธีการส่วนตัวนอกคลาสเดิมเป็นปัญหาหลัก และยังมีข้อ จำกัด ในการเยาะเย้ยวิธีส่วนตัวในเครื่องมือการเยาะเย้ยบางอย่าง (เช่นMockito )
แม้ว่าจะมีเครื่องมือบางอย่างเช่นPower Mockที่รองรับสิ่งนั้น แต่มันเป็นการทำงานที่อันตราย เหตุผลก็คือต้องทำการแฮ็ค JVM เพื่อให้บรรลุ
วิธีแก้ปัญหาหนึ่งที่สามารถทำได้คือ (ถ้าคุณต้องการเขียนกรณีทดสอบสำหรับวิธีส่วนตัว)
ประกาศผู้ส่วนตัววิธีการตามที่ได้รับการคุ้มครอง แต่อาจไม่สะดวกสำหรับหลาย ๆ สถานการณ์
ไม่เพียงเกี่ยวกับวิธีการหรือฟังก์ชั่นสาธารณะหรือส่วนตัว แต่ยังเกี่ยวกับรายละเอียดการใช้งาน ฟังก์ชั่นส่วนตัวเป็นเพียงส่วนหนึ่งของรายละเอียดการใช้งาน
หลังจากการทดสอบหน่วยเป็นวิธีทดสอบกล่องสีขาว ตัวอย่างเช่นใครก็ตามที่ใช้การวิเคราะห์ความครอบคลุมเพื่อระบุส่วนต่าง ๆ ของรหัสที่ถูกละเลยในการทดสอบจนถึงรายละเอียดการนำไปปฏิบัติ
A) ใช่คุณควรทดสอบรายละเอียดการใช้งาน:
นึกถึงฟังก์ชั่นการเรียงลำดับซึ่งสำหรับเหตุผลด้านประสิทธิภาพใช้การใช้งานส่วนตัวของ BubbleSort หากมีถึง 10 องค์ประกอบและการใช้งานแบบส่วนตัวของวิธีการเรียงลำดับที่แตกต่างกัน (เช่น heapsort) หากมีมากกว่า 10 องค์ประกอบ API สาธารณะเป็นหน้าที่ของการเรียงลำดับ อย่างไรก็ตามชุดทดสอบของคุณใช้ประโยชน์จากความรู้ที่มีอัลกอริธึมการเรียงลำดับสองแบบที่ใช้จริง
ในตัวอย่างนี้คุณสามารถทำการทดสอบกับ API สาธารณะได้ อย่างไรก็ตามสิ่งนี้จะต้องมีกรณีทดสอบจำนวนหนึ่งที่เรียกใช้ฟังก์ชันการเรียงลำดับที่มีมากกว่า 10 องค์ประกอบเช่นอัลกอริทึม heapsort นั้นได้รับการทดสอบอย่างเพียงพอแล้ว การมีอยู่ของกรณีทดสอบเพียงอย่างเดียวนั้นเป็นข้อบ่งชี้ว่าชุดทดสอบเชื่อมต่อกับรายละเอียดการใช้งานของฟังก์ชั่น
หากรายละเอียดการนำไปปฏิบัติของฟังก์ชั่นการเรียงลำดับเปลี่ยนอาจเป็นไปได้ว่าวิธีการเปลี่ยนขีด จำกัด ระหว่างอัลกอริธึมการเรียงลำดับทั้งสองหรือการแทนที่ฮีปพอร์ตถูกแทนที่ด้วยการผสานหรืออะไรก็ตาม: การทดสอบที่มีอยู่จะยังคงทำงานต่อไป ค่าของพวกเขายังคงเป็นที่น่าสงสัยและพวกเขามีแนวโน้มที่จะต้องทำใหม่เพื่อทดสอบฟังก์ชั่นการเรียงลำดับที่เปลี่ยนแปลง กล่าวอีกนัยหนึ่งจะมีความพยายามในการบำรุงรักษาแม้จะมีการทดสอบใน API สาธารณะ
B) วิธีทดสอบรายละเอียดการใช้งาน
เหตุผลหนึ่งที่หลายคนโต้แย้งไม่ควรทดสอบฟังก์ชั่นส่วนตัวหรือรายละเอียดการใช้งานคือว่ารายละเอียดการใช้งานมีแนวโน้มที่จะเปลี่ยนแปลง ความเป็นไปได้ของการเปลี่ยนแปลงที่สูงขึ้นอย่างน้อยนี้เป็นหนึ่งในสาเหตุของการซ่อนรายละเอียดการใช้งานไว้ด้านหลังอินเตอร์เฟส
ทีนี้สมมติว่าการนำไปใช้งานหลังส่วนต่อประสานนั้นมีชิ้นส่วนส่วนตัวที่ใหญ่กว่าซึ่งการทดสอบส่วนบุคคลในส่วนต่อประสานภายในอาจเป็นตัวเลือก บางคนเถียงส่วนเหล่านี้ไม่ควรทดสอบเมื่อส่วนตัวพวกเขาควรจะกลายเป็นสิ่งสาธารณะ เมื่อสาธารณะแล้วการทดสอบหน่วยนั้น ๆ รหัสนั้นก็จะตกลง
สิ่งนี้เป็นสิ่งที่น่าสนใจ: แม้ว่าอินเทอร์เฟซภายในจะมีแนวโน้มที่จะเปลี่ยนไป การใช้อินเทอร์เฟซเดียวกันทำให้เป็นที่สาธารณะโดยใช้การแปลงเวทย์มนตร์บางอย่างกล่าวคือเปลี่ยนเป็นอินเทอร์เฟซที่มีโอกาสน้อยที่จะเปลี่ยนแปลง เห็นได้ชัดว่ามีข้อบกพร่องในการโต้แย้งนี้
แต่มีความจริงบางอย่างที่อยู่เบื้องหลังสิ่งนี้: เมื่อทดสอบรายละเอียดการใช้งานโดยเฉพาะอย่างยิ่งการใช้อินเทอร์เฟซภายในหนึ่งควรพยายามใช้อินเทอร์เฟซที่น่าจะยังคงมีเสถียรภาพ ไม่ว่าจะเป็นอินเทอร์เฟซบางอย่างที่มีแนวโน้มว่าจะเสถียรหรือไม่ก็ตามไม่สามารถตัดสินใจได้ง่าย ๆ ว่าขึ้นอยู่กับว่าเป็นสาธารณะหรือส่วนตัว ในโครงการต่าง ๆ จากโลกที่ฉันทำงานมาระยะหนึ่งอินเทอร์เฟซสาธารณะก็เปลี่ยนแปลงบ่อยพอและอินเทอร์เฟซส่วนตัวจำนวนมากยังคงไม่ถูกแตะต้องมานาน
ถึงกระนั้นก็เป็นกฎที่ดีที่จะใช้ "ประตูหน้าแรก" (ดูhttp://xunitpatterns.com/Principles%20of%20Test%20Automation.html ) แต่โปรดจำไว้ว่ามันถูกเรียกว่า "ประตูหน้าแรก" และไม่ใช่ "ประตูหน้าเท่านั้น"
C) สรุป
ทดสอบรายละเอียดการใช้งานด้วย ต้องการทดสอบอินเทอร์เฟซที่เสถียร (สาธารณะหรือส่วนตัว) หากมีการเปลี่ยนแปลงรายละเอียดการใช้งานการทดสอบ API สาธารณะจะต้องได้รับการแก้ไขอีกด้วย การเปลี่ยนอะไรบางอย่างให้เป็นสาธารณะไม่ได้เปลี่ยนความเสถียรอย่างน่าอัศจรรย์
ใช่คุณควรทดสอบวิธีการส่วนตัวหากเป็นไปได้ ทำไม? เพื่อหลีกเลี่ยงการระเบิดของพื้นที่ทดสอบในกรณีที่ไม่จำเป็นซึ่งท้ายที่สุดก็เป็นการสิ้นสุดการทดสอบฟังก์ชั่นส่วนตัวเดียวกันซ้ำ ๆ ในอินพุตเดียวกัน เรามาอธิบายกันว่าทำไมกับตัวอย่าง
ลองพิจารณาตัวอย่างที่มีการประดิษฐ์เล็กน้อยต่อไปนี้ สมมติว่าเราต้องการแสดงฟังก์ชั่นต่อสาธารณะที่ใช้จำนวนเต็ม 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
และไม่ได้หรือisPrime
andAll
< 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
มีความเป็นอิสระอย่างแท้จริงดังนั้นการทดสอบชุดค่าผสมทุกอย่างแบบสุ่มนั้นไม่มีจุดประสงค์ ประการที่สองการทำเครื่องหมายฟังก์ชั่นบริสุทธิ์ที่เรียกว่าisPrime
Private ละเมิดกฎการออกแบบมากมายที่ฉันไม่รู้ด้วยซ้ำว่าจะเริ่มต้นอย่างไร isPrime
ควรเป็นฟังก์ชั่นสาธารณะอย่างชัดเจน ที่ถูกกล่าวว่าฉันจะได้รับสิ่งที่คุณพูดโดยไม่คำนึงถึงตัวอย่างที่น่าสงสารอย่างยิ่งนี้ อย่างไรก็ตามมันสร้างขึ้นจากสถานที่ตั้งที่คุณต้องการทำการทดสอบแบบรวมกันเมื่อในระบบซอฟต์แวร์จริงมันไม่ค่อยเป็นความคิดที่ดี
นอกจากนี้คุณยังสามารถสร้างวิธีการแพคเกจส่วนตัวของคุณเช่นค่าเริ่มต้นและคุณควรจะสามารถทดสอบหน่วยเว้นแต่ว่าจะต้องเป็นส่วนตัว