(ทำไม) เป็นสิ่งสำคัญที่การทดสอบหน่วยไม่ทดสอบการพึ่งพา?


103

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

การเยาะเย้ย / การขัดถูเพื่อหลีกเลี่ยงการทดสอบการพึ่งพาเพิ่มความซับซ้อนให้กับการทดสอบ มันเพิ่มความยืดหยุ่นในการประดิษฐ์ / การแยกชิ้นส่วนข้อกำหนดในรหัสการผลิตของคุณเพื่อรองรับการเยาะเย้ย (ฉันไม่เห็นด้วยกับใครก็ตามที่บอกว่าสิ่งนี้ส่งเสริมการออกแบบที่ดีเขียนโค้ดพิเศษแนะนำสิ่งต่าง ๆ เช่นกรอบการฉีดพึ่งพาหรือเพิ่มความซับซ้อนให้กับโค้ดเบสของคุณเพื่อให้สิ่งที่ยืดหยุ่น / pluggable / extensible / decoupled การออกแบบที่ดี.)

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

ด้านนี้คืออะไร? เป็นสิ่งสำคัญจริง ๆ ที่การทดสอบหน่วยไม่ได้ทดสอบการพึ่งพาของมันด้วย? ถ้าเป็นเช่นนั้นทำไม

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


14
"overengineering ไม่ใช่การออกแบบที่ดี" คุณจะต้องให้หลักฐานมากกว่านั้น คนจำนวนมากจะไม่เรียกมันว่า "เหนือวิศวกรรม" แต่ "แนวปฏิบัติที่ดีที่สุด"
S.Lott

9
@ S.Lott: แน่นอนว่ามันเป็นเรื่องส่วนตัว ฉันแค่ไม่ต้องการคำตอบมากมายที่ทำให้เกิดปัญหาและการพูดว่าการเยาะเย้ยเป็นสิ่งที่ดีเพราะการทำให้โค้ดของคุณเป็นการเยาะเย้ยส่งเสริมการออกแบบที่ดี โดยทั่วไปแล้วฉันเกลียดการจัดการกับโค้ดที่แยกออกในรูปแบบที่ไม่มีประโยชน์ชัดเจนในตอนนี้หรือในอนาคตอันใกล้ หากคุณยังไม่มีการใช้งานหลายอย่างในขณะนี้และไม่คาดหวังว่าจะมีการใช้งานในอนาคตอันใกล้ IMHO คุณควรเขียนโค้ดอย่างหนัก มันง่ายกว่าและไม่เจาะลูกค้าด้วยรายละเอียดการพึ่งพาของวัตถุ
dsimcha

5
โดยทั่วไปแล้วฉันเกลียดการจัดการกับโค้ดที่มีวิธีการที่ไม่มีเหตุผลชัดเจนยกเว้นการออกแบบที่ไม่ดี มันง่ายกว่าและไม่เจาะลูกค้าด้วยความยืดหยุ่นในการทดสอบสิ่งต่าง ๆ
S.Lott

3
@ S.Lott: ชี้แจง: ฉันหมายถึงรหัสที่ออกไปอย่างมีนัยสำคัญในการแยกสิ่งต่าง ๆ โดยไม่ต้องใช้กรณีที่ชัดเจนโดยเฉพาะอย่างยิ่งที่มันทำให้รหัสหรือรหัสลูกค้าของ verbose มากขึ้นอย่างมีนัยสำคัญแนะนำคลาสอื่น / อินเตอร์เฟส ฯลฯ แน่นอนฉันไม่ได้เรียกร้องให้มีรหัสที่แน่นหนากว่าการออกแบบที่เรียบง่ายและรัดกุมที่สุด นอกจากนี้เมื่อคุณสร้างบรรทัดของสิ่งที่เป็นนามธรรมก่อนกำหนดพวกเขามักจะจบลงในสถานที่ที่ไม่ถูกต้อง
dsimcha

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

คำตอบ:


116

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

สำหรับผลิตภัณฑ์ของเรา: การทดสอบหน่วยของเราดำเนินการกับแต่ละบิลด์ใช้เวลาไม่กี่วินาที ชุดย่อยของการทดสอบการรวมระบบของเราทำงานด้วยการเช็คอินแต่ละครั้งใช้เวลา 10 นาที ชุดอินทิเกรตเต็มรูปแบบของเราเปิดให้บริการทุกคืนใช้เวลา 4 ชั่วโมง


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

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

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

2
นอกจากนี้ยังเป็นสิ่งสำคัญที่จะต้องเข้าใจแนวคิดของการทดสอบซุ้มที่ไม่ได้ทดสอบว่าส่วนประกอบเข้ากันได้อย่างไร (เช่นการทดสอบการรวม) แต่ทดสอบการแยกองค์ประกอบเดียว (เช่นกราฟวัตถุ)
Ricardo Rodrigues

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

39

การทดสอบกับการพึ่งพาทั้งหมดในสถานที่ยังคงมีความสำคัญ แต่ก็มีมากขึ้นในขอบเขตของการทดสอบการรวมเป็นเจฟฟรีย์เฟาสท์กล่าวว่า

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

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

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

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

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


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

public class MyClass 
{
    private SomeClass someClass;
    public MyClass()
    {
        someClass = new SomeClass();
    }

    // use someClass in some way
}

โดยทั่วไปฉันไม่สนใจว่าSomeClassจะสร้างอย่างไร ฉันแค่อยากจะใช้มัน ถ้า SomeClass เปลี่ยนไปและตอนนี้ต้องการพารามิเตอร์ให้กับ Constructor ... นั่นไม่ใช่ปัญหาของฉัน ฉันไม่ควรเปลี่ยน MyClass เพื่อรองรับสิ่งนั้น

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

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

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


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

@dsimcha ฉันขยายคำตอบของฉันเพื่อดูรายละเอียดเพิ่มเติมเกี่ยวกับเรื่องนั้น หวังว่ามันจะช่วย
Adam Lear

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

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

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

22

นอกเหนือจากปัญหาการทดสอบหน่วย vs Integration พิจารณาต่อไปนี้

วิดเจ็ตชั้นเรียนมีการพึ่งพาชั้นเรียน Thingamajig และ WhatsIt

การทดสอบหน่วยสำหรับ Widget ล้มเหลว

ปัญหาอยู่ที่ระดับใด?

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


3
@Brook: วิธีการเกี่ยวกับการดูสิ่งที่มีผลสำหรับการอ้างอิงของ Widget ทั้งหมดหรือไม่ หากพวกเขาผ่านมันเป็นปัญหากับ Widget จนกว่าจะพิสูจน์เป็นอย่างอื่น
dsimcha

4
@dsimcha แต่ตอนนี้คุณเพิ่มความซับซ้อนในเกมเพื่อตรวจสอบขั้นตอนกลาง ทำไมไม่ลดความซับซ้อนและทำการทดสอบหน่วยง่ายๆก่อน? จากนั้นทำการทดสอบการรวมของคุณ
asoundmove

1
@dsimcha ซึ่งฟังดูสมเหตุสมผลจนกว่าคุณจะได้รับกราฟการพึ่งพาที่ไม่สำคัญ สมมติว่าคุณมีวัตถุที่ซับซ้อนที่มีความลึกมากกว่า 3 ชั้นพร้อมการพึ่งพามันจะเปลี่ยนเป็น O (N ^ 2) เพื่อค้นหาปัญหาแทนที่จะเป็น O (1)
Brook

1
นอกจากนี้การตีความ SRP ของฉันก็คือชั้นควรมีความรับผิดชอบเดียวในระดับโดเมนแนวความคิด / ปัญหา มันไม่สำคัญว่าการเติมเต็มความรับผิดชอบนั้นจะต้องทำสิ่งต่าง ๆ มากมายเมื่อมองในระดับที่เป็นนามธรรม หากนำไปใช้อย่างรุนแรง SRP จะตรงกันข้ามกับ OO เนื่องจากคลาสส่วนใหญ่เก็บข้อมูลและดำเนินการกับมัน (สองสิ่งเมื่อดูในระดับต่ำพอสมควร)
dsimcha

4
@Brook: ประเภทอื่น ๆ ไม่ได้เพิ่มความซับซ้อนต่อ se ประเภทอื่น ๆ นั้นใช้ได้ถ้าประเภทเหล่านั้นทำให้เกิดแนวคิดในโดเมนปัญหาและทำให้รหัสง่ายขึ้นไม่ยากที่จะเข้าใจ ปัญหาคือการแยกสิ่งต่าง ๆ ที่มีการเชื่อมโยงกับแนวคิดในระดับโดเมนปัญหา (เช่นคุณมีเพียงการใช้งานเพียงอย่างเดียวอาจจะไม่มีมากกว่าหนึ่ง ฯลฯ ) และการแยกสัญญาณไม่ตรงกับแนวคิดของปัญหาโดเมน ในกรณีเหล่านี้มันเป็นเรื่องไร้สาระระบบราชการและ verbose เพื่อสร้าง abstractions ที่ร้ายแรงเกี่ยวกับการใช้งานนี้
dsimcha

14

ลองนึกภาพว่าการเขียนโปรแกรมเหมือนการทำอาหาร จากนั้นการทดสอบหน่วยจะเหมือนกับการทำให้แน่ใจว่าส่วนผสมของคุณสดอร่อย ฯลฯ ในขณะที่การทดสอบการรวมเข้าด้วยกันทำให้แน่ใจว่าอาหารของคุณอร่อย

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

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


7
และเมื่อการทดสอบรวมของคุณล้มเหลวการทดสอบหน่วยอาจเป็นศูนย์ในปัญหา
ทิม Williscroft

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

รักการเปรียบเทียบนี้!
thehowler

6

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

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

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

ดังนั้นโดยย่อสามระดับของ "การทดสอบหน่วย" ฉันมักจะใช้ในโครงการเกือบทั้งหมดของฉัน:

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

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


4

ด้านนี้คืออะไร? เป็นสิ่งสำคัญจริง ๆ ที่การทดสอบหน่วยไม่ได้ทดสอบการพึ่งพาของมันด้วย? ถ้าเป็นเช่นนั้นทำไม

หน่วย หมายถึงเอกพจน์

การทดสอบ 2 สิ่งนั้นหมายความว่าคุณมีสองสิ่งและการพึ่งพาการทำงานทั้งหมด

หากคุณเพิ่มสิ่งที่ 3 คุณจะเพิ่มการพึ่งพาการทำงานในการทดสอบเกินกว่าจะเป็นเส้นตรง การเชื่อมโยงระหว่างสิ่งต่าง ๆ เติบโตเร็วกว่าจำนวนของสิ่งต่าง ๆ

มี n (n-1) / 2 dependencices ที่อาจเกิดขึ้นในหมู่nรายการที่ถูกทดสอบ

นั่นเป็นเหตุผลใหญ่

ความเรียบง่ายมีค่า


2

โปรดจำไว้ว่าคุณเรียนรู้ที่จะเรียนรู้การเรียกซ้ำครั้งแรกอย่างไร ศาสตราจารย์ของฉันพูดว่า "สมมติว่าคุณมีวิธีที่ทำ x" (เช่นแก้ fibbonacci สำหรับ x ใด ๆ ) "เพื่อแก้ปัญหาสำหรับ x คุณต้องเรียกใช้วิธีนั้นสำหรับ x-1 และ x-2" ในแง่เดียวกันการขัดจังหวะการพึ่งพาช่วยให้คุณแกล้งพวกเขามีอยู่และทดสอบว่าหน่วยปัจจุบันทำในสิ่งที่ควรทำ สมมติฐานนี้แน่นอนว่าคุณกำลังทดสอบการพึ่งพาอย่างจริงจัง

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


2

(นี่เป็นคำตอบเล็กน้อยขอบคุณ @ TimWilliscroft สำหรับคำใบ้)

ความผิดพลาดง่ายกว่าในการแปลหาก:

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

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


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

2

คำตอบที่ดีมาก ฉันจะเพิ่มจุดอื่นสองสาม:

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

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


0

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

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

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

0

เอ่อ ... มีจุดที่ดีในการทดสอบหน่วยและการรวมกลุ่มที่เขียนในคำตอบเหล่านี้ที่นี่

ฉันคิดถึงมุมมองที่เกี่ยวข้องกับต้นทุนและการปฏิบัติที่นี่ ที่กล่าวว่าฉันเห็นชัดเจนประโยชน์ของการทดสอบหน่วยแยก / อะตอมมาก (อาจเป็นอิสระจากกันและมีตัวเลือกให้ทำงานในแบบคู่ขนานและไม่มีการอ้างอิงใด ๆ เช่นฐานข้อมูลระบบแฟ้ม ฯลฯ ) และ (ระดับสูง) การทดสอบการรวม แต่ ... ก็ยังเป็นเรื่องของค่าใช้จ่าย (เวลา, เงิน, ... ) และความเสี่ยง

ดังนั้นจึงมีปัจจัยอื่น ๆ ที่สำคัญกว่า (เช่น"สิ่งที่ต้องทดสอบ" ) ก่อนที่คุณจะคิดถึง"วิธีการทดสอบ"จากประสบการณ์ของฉัน ...

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

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

มาถึงสุดขีดที่เพื่อนร่วมงานในทีมของฉันแนะนำเราต้องลองทดสอบหน่วยเลเยอร์โค้ด Java EE แท้ของเราด้วยการครอบคลุม 100% ที่ต้องการสำหรับคลาสทั้งหมดภายในและการเยาะเย้ยฐานข้อมูล หรือผู้จัดการที่ต้องการทดสอบการรวมจะครอบคลุม 100% ของทุกกรณีการใช้งานจริงและเวิร์กโฟลว์ web UI เพราะเราไม่ต้องการเสี่ยงกรณีการใช้งานใด ๆ ที่จะล้มเหลว แต่เรามีงบประมาณที่แน่นอยู่ที่ประมาณ 1 ล้านยูโรค่อนข้างจะค่อนข้างรัดกุมในการเขียนโค้ดทุกอย่าง สภาพแวดล้อมของลูกค้าที่ข้อบกพร่องของแอพอาจไม่เป็นอันตรายต่อมนุษย์หรือ บริษัท แอปของเราจะได้รับการทดสอบภายในด้วย (บางส่วน) การทดสอบหน่วยสำคัญการทดสอบการรวมการทดสอบลูกค้าสำคัญพร้อมแผนการทดสอบที่ออกแบบมาระยะการทดสอบ ฯลฯ เราไม่พัฒนาแอพสำหรับโรงงานนิวเคลียร์หรือการผลิตยา!

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

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

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

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

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