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


98

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

เมื่อคุณเขียนการทดสอบหน่วยสำหรับ A คุณจำลอง X ในคำอื่น ๆ ในขณะที่การทดสอบหน่วย A คุณตั้งค่า (สมมุติ) พฤติกรรมของจำลองของ X ให้เป็น X1 เวลาผ่านไปผู้คนใช้ระบบของคุณต้องการการเปลี่ยนแปลง X วิวัฒนาการ: คุณแก้ไข X เพื่อแสดงพฤติกรรม X2 เห็นได้ชัดว่าการทดสอบหน่วยสำหรับ X จะล้มเหลวและคุณจะต้องปรับตัว

แต่อะไรกับ A? การทดสอบหน่วยสำหรับ A จะไม่ล้มเหลวเมื่อปรับเปลี่ยนพฤติกรรมของ X (เนื่องจากการเยาะเย้ยของ X) วิธีการตรวจสอบว่าผลลัพธ์ของ A จะแตกต่างกันเมื่อทำงานด้วย X "จริง" (แก้ไข) X

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



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

5
การทดสอบหน่วยจะบอกคุณว่าหน่วยรหัสของคุณทำงานตามที่คุณคาดหวังหรือไม่ ไม่มากหรือน้อย Mocks และคู่ทดสอบมีสภาพแวดล้อมเทียมที่ควบคุมได้เพื่อให้คุณใช้หน่วยของรหัส (แยกกัน) เพื่อดูว่าตรงตามความคาดหวังของคุณหรือไม่ ไม่มากหรือน้อย
Robert Harvey

2
ฉันเชื่อว่าหลักฐานของคุณไม่ถูกต้อง เมื่อคุณพูดถึงX1คุณจะบอกว่าดำเนินการติดต่อX X1ถ้าคุณเปลี่ยนอินเตอร์เฟซX1ที่จะX2เยาะเย้ยที่คุณใช้ในการทดสอบอื่น ๆ ที่ไม่ควรรวบรวมอีกต่อไปด้วยเหตุนี้คุณจะถูกบังคับให้แก้ไขการทดสอบเหล่านั้นมากเกินไป การเปลี่ยนแปลงพฤติกรรมในชั้นเรียนไม่ควรสำคัญ ในความเป็นจริงคลาสของคุณAไม่ควรขึ้นอยู่กับรายละเอียดการใช้งาน (ซึ่งเป็นสิ่งที่คุณต้องการเปลี่ยนแปลงในกรณีนั้น) ดังนั้นการทดสอบหน่วยAยังคงถูกต้องและพวกเขาบอกคุณว่าการAทำงานได้รับการใช้งานที่เหมาะสมของอินเทอร์เฟซ
Bakuriu

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

คำตอบ:


125

เมื่อคุณเขียนการทดสอบหน่วยสำหรับ A คุณจำลอง X

คุณ ฉันทำไม่ได้เว้นแต่จะต้องทำอย่างแน่นอน ฉันต้องถ้า:

  1. X ช้าหรือ
  2. X มีผลข้างเคียง

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

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

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


94
@Laiv, ไม่มี, การทดสอบหน่วยควรจะทำหน้าที่เป็นหน่วยเช่นเรียกใช้ในการแยกจากการทดสอบอื่นโหนดและกราฟสามารถไต่ขึ้นไปได้ หากฉันสามารถทำการทดสอบแบบ end-to-end แบบแยกได้และไม่มีผลข้างเคียงในเวลาอันสั้นนั่นคือการทดสอบหน่วย ในถ้าคุณไม่ชอบคำจำกัดความนี้ให้เรียกมันว่าการทดสอบอัตโนมัติและหยุดเขียนการทดสอบอึให้พอดีกับความหมายที่เซ่อ
David Arno

9
@DavidArno ยังมีคำจำกัดความที่กว้างมากเกี่ยวกับการแยก บางคนต้องการ "หน่วย" เพื่อรวมระดับกลางและฐานข้อมูล พวกเขาสามารถเชื่อในสิ่งที่พวกเขาชอบ แต่โอกาสที่จะเกิดขึ้นเมื่อมีการพัฒนาในทุกขนาดล้อจะหลุดออกมาในเวลาอันสั้นเนื่องจากผู้สร้างบิลด์จะโยนมันออกไป โดยทั่วไปหากแยกออกจากการชุมนุม (หรือเทียบเท่า) นั่นเป็นเรื่องปกติ หมายเหตุถ้าคุณใช้รหัสในส่วนต่อประสานจะเป็นการเพิ่มการเยาะเย้ยและ DI ในภายหลัง
Robbie Dee

13
คุณกำลังสนับสนุนการทดสอบประเภทอื่นมากกว่าการตอบคำถาม ซึ่งเป็นจุดที่ถูกต้อง แต่นี่เป็นวิธีการเล่ห์เหลี่ยมค่อนข้างทำ
Phil

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

30
ฉันอยู่กับ @DavidArno ในอันนี้; กลยุทธ์การทดสอบของฉันเปลี่ยนหลังจากดูการพูดคุยนี้โดยเอียนคูเปอร์: vimeo.com/68375232 การต้มมันลงไปประโยคเดียว: ไม่ได้ทดสอบการเรียน การทดสอบพฤติกรรม การทดสอบของคุณไม่ควรมีความรู้เกี่ยวกับคลาส / วิธีการภายในที่ใช้ในการใช้งานพฤติกรรมที่ต้องการ พวกเขาควรรู้พื้นผิวสาธารณะของ API / ไลบรารีของคุณเท่านั้นและควรทดสอบสิ่งนั้น หากการทดสอบมีความรู้มากเกินไปคุณจะทำการทดสอบรายละเอียดการใช้งานและการทดสอบของคุณจะเปราะบางควบคู่ไปกับการใช้งานของคุณ
Richiban

79

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

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

ด้วยการทดสอบหน่วยคุณจะต้องทดสอบ 20 หน่วย + การทดสอบการรวม 1 เพื่อครอบคลุมทุกกรณีอย่างสมบูรณ์

ด้วยการทดสอบการรวมคุณจะต้อง 100 การทดสอบเพื่อให้ครอบคลุมทุกเส้นทางรหัส

นี่คือวิดีโอที่ดีมากเกี่ยวกับข้อเสียของการใช้การทดสอบการรวมเท่านั้นJB Rainsberger การทดสอบแบบรวมเป็น Scam HD


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

16
ถูกต้อง แต่ไม่มีหน่วยทดสอบ 20 หน่วยของคุณต้องการการเยาะเย้ย หากคุณมีการทดสอบ A จำนวน 10 ข้อซึ่งครอบคลุมการทดสอบ A ทั้งหมดและการทดสอบ 10 รายการของคุณที่ครอบคลุมการทดสอบ B ทั้งหมดและยังทดสอบซ้ำ 25% ของ A เป็นโบนัสซึ่งดูเหมือนว่าจะเป็น "ดี" และเป็นสิ่งที่ดี การเยาะเย้ย A ในการทดสอบ Bs ดูเหมือนโง่อย่างแข็งขัน (เว้นแต่จะมีเหตุผลจริงๆที่ว่า A เป็นปัญหาเช่นเป็นฐานข้อมูลหรือนำเว็บอื่น ๆ
Richard Tingle

9
ฉันไม่เห็นด้วยกับความคิดที่ว่าการทดสอบบูรณาการครั้งเดียวก็เพียงพอหากคุณต้องการความคุ้มครองเต็ม ปฏิกิริยาของ B กับเอาท์พุตจะแตกต่างกันไปตามเอาท์พุท; หากการเปลี่ยนพารามิเตอร์ใน A เป็นการเปลี่ยนเอาท์พุต B อาจไม่สามารถจัดการได้อย่างถูกต้อง
Matthieu M.

3
@ Eternal21: ประเด็นของฉันคือบางครั้งปัญหาไม่ได้อยู่ในพฤติกรรมของแต่ละบุคคล แต่เป็นการโต้ตอบที่ไม่คาดคิด นั่นคือเมื่อกาวระหว่าง A และ B ทำงานโดยไม่คาดคิดในบางสถานการณ์ ดังนั้นทั้ง A และ B จึงทำหน้าที่เป็นไปตามข้อกำหนดและงานเคสที่มีความสุข แต่ในบางอินพุตมีข้อผิดพลาดในโค้ดกาว ...
Matthieu M.

1
@MatthieuM ฉันเถียงว่ามันเกินขอบเขตของการทดสอบหน่วย รหัสกาวสามารถทดสอบหน่วยในขณะที่การโต้ตอบระหว่าง A และ B ผ่านรหัสกาวคือการทดสอบการรวม เมื่อพบเคสหรือบั๊กที่เฉพาะเจาะจงพวกเขาสามารถเพิ่มลงในการทดสอบหน่วยรหัสกาวและตรวจสอบในที่สุดในการทดสอบการรวม
Andrew T Finnell

72

เมื่อคุณเขียนการทดสอบหน่วยสำหรับ A คุณจำลอง X ในคำอื่น ๆ ในขณะที่การทดสอบหน่วย A คุณตั้งค่า (สมมุติ) พฤติกรรมของจำลองของ X ให้เป็น X1 เวลาผ่านไปผู้คนใช้ระบบของคุณต้องการการเปลี่ยนแปลง X วิวัฒนาการ: คุณแก้ไข X เพื่อแสดงพฤติกรรม X2 เห็นได้ชัดว่าการทดสอบหน่วยสำหรับ X จะล้มเหลวและคุณจะต้องปรับตัว

ว้าวเดี๋ยวก่อน ความหมายของการทดสอบ X ที่ล้มเหลวนั้นสำคัญเกินกว่าที่จะปัดเศษแบบนั้น

หากการเปลี่ยนการใช้งาน X จาก X1 เป็น X2 ทำให้การทดสอบหน่วยสำหรับ X สิ้นสุดลงแสดงว่าคุณได้ทำการเปลี่ยนแปลงย้อนหลังกับสัญญา X

X2 ไม่ใช่ X ในแง่ของ Liskovดังนั้นคุณควรคิดถึงวิธีอื่น ๆ ในการตอบสนองความต้องการของผู้ถือหุ้นของคุณ (เช่นการแนะนำข้อกำหนดใหม่ Y ซึ่งดำเนินการโดย X2)

สำหรับข้อมูลเชิงลึกลึกเห็นปีเตอร์ Hinjens: จุดจบของรุ่นซอฟแวร์หรือรวย Hickey ง่าย Made Easy

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

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

บางครั้งคุณจะเห็นความแตกต่างนี้แสดงเป็นการทดสอบแบบเดี่ยวและแบบsociableทดสอบ เห็นเจย์ทุ่งทำงานอย่างมีประสิทธิภาพกับหน่วยทดสอบ

เราไม่ควรมุ่งเน้นไปที่การทดสอบการรวมระบบมากกว่านี้ใช่ไหม

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

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


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

1
"หากไม่มีการทดสอบแบบแยก / เดี่ยวที่ใช้แรงกดดันในการออกแบบคุณภาพจะลดลง" ฉันคิดว่านี่เป็นจุดสำคัญ นอกเหนือจากการตรวจสอบพฤติกรรมการทดสอบยูนิตมีผลข้างเคียงที่จะบังคับให้คุณมีการออกแบบแบบแยกส่วนเพิ่มเติม
MickaëlG

ฉันคิดว่านี่เป็นความจริงทั้งหมด แต่สิ่งนี้จะช่วยฉันได้อย่างไรหากการพึ่งพาจากภายนอกนำเสนอการเปลี่ยนแปลงที่ไม่สอดคล้องกันของสัญญา X หลัง บางทีคลาส I / O ที่มีประสิทธิภาพในไลบรารีจะหยุดความเข้ากันได้และเรากำลังล้อเลียน X เพราะเราไม่ต้องการให้หน่วยทดสอบใน CI ขึ้นอยู่กับ I / O หนัก ฉันคิดว่า OP ขอให้ทดสอบสิ่งนี้และฉันไม่เข้าใจว่าสิ่งนี้ตอบคำถามได้อย่างไร วิธีทดสอบสำหรับสิ่งนี้?
gerrit

15

ผมขอเริ่มด้วยการบอกว่าหลักฐานหลักของคำถามนั้นมีข้อบกพร่อง

คุณยังไม่เคยทดสอบ (หรือเยาะเย้ย) การใช้งานที่คุณกำลังทดสอบ (และเยาะเย้ย) อินเตอร์เฟซ

ถ้าฉันมีคลาส X ที่ใช้อินเทอร์เฟซ X1 ฉันสามารถเขียนจำลอง XM ที่สอดคล้องกับ X1 ได้ ถ้าอย่างนั้นคลาส A ของฉันต้องใช้สิ่งที่ใช้ X1 ซึ่งอาจเป็นคลาส X หรือเยาะเย้ย XM

ทีนี้สมมติว่าเราเปลี่ยน X เพื่อใช้งานอินเตอร์เฟสใหม่ X2 เห็นได้ชัดว่ารหัสของฉันไม่ได้รวบรวมอีกต่อไป A ต้องการสิ่งที่ใช้ X1 และไม่มีอยู่อีกต่อไป มีการระบุปัญหาและสามารถแก้ไขได้

สมมติว่าแทนที่จะเปลี่ยน X1 เราแค่แก้ไขมัน ตอนนี้คลาส A พร้อมใช้งานแล้ว อย่างไรก็ตาม mock XM ไม่ได้ใช้อินเตอร์เฟส X1 อีกต่อไป มีการระบุปัญหาและสามารถแก้ไขได้


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

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


11
คำตอบนี้ทำให้สมมติฐานมากมายที่ไม่จำเป็นต้องถือ อย่างแรกคือถือว่าโดยประมาณว่าเราอยู่ใน C # หรือ Java (หรือแม่นยำยิ่งขึ้นว่าเราอยู่ในภาษาที่คอมไพล์ภาษานั้นมีอินเตอร์เฟสและ X ใช้อินเทอร์เฟซไม่จำเป็นต้องเป็นจริง) ประการที่สองมันถือว่าการเปลี่ยนแปลงใด ๆ กับพฤติกรรมหรือ "สัญญา" ของ X ต้องมีการเปลี่ยนแปลงอินเทอร์เฟซ (ตามที่เข้าใจโดยคอมไพเลอร์) ที่ X ดำเนินการ สิ่งนี้ไม่เป็นความจริงแม้ว่าเราจะอยู่ใน Java หรือ C # ก็ตาม คุณสามารถเปลี่ยนการใช้วิธีโดยไม่ต้องเปลี่ยนลายเซ็นของมัน
Mark Amery

6
@ MarkAmery มันเป็นความจริงที่คำศัพท์เฉพาะ "อินเตอร์เฟส" นั้นเฉพาะเจาะจงกับ C # หรือ Java แต่ฉันคิดว่าจุดยืนคือ "สัญญา" ที่กำหนดไว้สำหรับพฤติกรรม (และหากไม่ได้ประมวลผลโดยอัตโนมัติ นอกจากนี้คุณยังถูกต้องสมบูรณ์ว่าการดำเนินการสามารถเปลี่ยนแปลงได้โดยไม่ต้องเปลี่ยนแปลงสัญญา แต่การเปลี่ยนแปลงการใช้งานโดยไม่มีการเปลี่ยนแปลงอินเทอร์เฟซ (หรือสัญญา) จะไม่ส่งผลกระทบต่อผู้บริโภค หากลักษณะการทำงานของ A ขึ้นอยู่กับวิธีการใช้งานอินเทอร์เฟซ (หรือสัญญา) นั้นเป็นไปไม่ได้ที่จะทำการทดสอบหน่วย (อย่างมีความหมาย)
Vlad274

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

4
@ MarkAmery: ฉันไม่คิดว่า Vlad กำลังใช้คำว่า "interface" ในลักษณะเดียวกับที่คุณใช้ วิธีที่ฉันอ่านคำตอบมันไม่ได้พูดถึงส่วนต่อประสานในความรู้สึก C # / Java ที่แคบ (เช่นชุดของลายเซ็นวิธีการ) แต่ในความหมายทั่วไปของคำที่ใช้เช่นในคำว่า "การเขียนโปรแกรมประยุกต์อินเตอร์เฟส" หรือแม้แต่ " หน้าจอผู้ใช้". [... ]
Ilmari Karonen

6
@IlmariKaronen หาก Vlad กำลังใช้ "ส่วนต่อประสาน" เพื่อหมายถึง "สัญญา" มากกว่าในความหมาย C # / Java ที่แคบแล้วคำว่า "ทีนี้สมมติว่าเราเปลี่ยน X เพื่อใช้ส่วนต่อประสาน X2 ใหม่แน่นอนว่ารหัสของฉันไม่คอมไพล์อีกต่อไป " เป็นเท็จเพียงธรรมดาเนื่องจากคุณสามารถเปลี่ยนสัญญาได้โดยไม่ต้องเปลี่ยนลายเซ็นของวิธีการใด ๆ แต่อย่างสุจริตผมคิดว่าปัญหาที่เกิดขึ้นที่นี่เป็นที่ลาดไม่ได้ใช้อย่างใดอย่างหนึ่งความหมายอย่างต่อเนื่อง แต่มหันต์พวกเขา - ซึ่งเป็นสิ่งที่นำไปสู่เส้นทางของการอ้างว่าการเปลี่ยนแปลงที่จะทำสัญญา X1 ใด ๆ จำเป็นต้องจะทำให้เกิดข้อผิดพลาดในการรวบรวมโดยไม่สังเกตเห็นว่าที่เป็นเท็จ .
Mark Amery

9

รับคำถามของคุณด้วย:

การทดสอบหน่วยมีค่าเท่าไหร่แล้ว

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

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

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

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

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

เราไม่ควรมุ่งเน้นไปที่การทดสอบการรวมระบบมากกว่านี้ใช่ไหม

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

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

หากคุณต้องการให้แน่ใจว่าคลาส A ใช้คลาส X ในลักษณะเดียวกันคุณควรใช้อินเตอร์เฟสแทนการตัดสินใจ จากนั้นการเปลี่ยนแปลงที่เกิดขึ้นจะมีโอกาสมากขึ้นในการรวบรวม


9

ถูกต้อง.

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

การทดสอบหน่วยไม่ได้มีไว้เพื่อทดสอบว่าแอปพลิเคชันทั้งหมดทำงานได้หรือไม่

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

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

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

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


2

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

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


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

7
@ RobbieDee ฉันเดาว่าเขาหมายความว่าเมื่อคุณทดสอบfac(5) == 120คุณยังไม่ได้พิสูจน์ว่าfac()จริง ๆ แล้วคืนค่าแฟคทอเรียลของอาร์กิวเมนต์ คุณได้รับการพิสูจน์เดียวที่ผลตอบแทนปัจจัยห้าเมื่อคุณผ่านในfac() 5และถึงแม้จะไม่แน่ใจเท่าที่fac()ควรจะกลับมา42แทนในวันจันทร์แรกหลังจากคราสรวมใน Timbuktu ... ปัญหาที่นี่คือว่าคุณไม่สามารถพิสูจน์การปฏิบัติตามโดยการตรวจสอบอินพุตทดสอบแต่ละตัวคุณจะต้องตรวจสอบอินพุตที่เป็นไปได้ทั้งหมดและพิสูจน์ด้วยว่าคุณยังไม่ลืมเลย (เช่นการอ่านนาฬิการะบบ)
cmaster

1
@RobbieDee Tests (รวมถึงการทดสอบหน่วย) เป็นตัวแทนที่ไม่ดีซึ่งมักจะดีที่สุดสำหรับเป้าหมายที่แท้จริงเครื่องตรวจสอบหลักฐาน พิจารณาพื้นที่รัฐทั้งหมดของหน่วยที่อยู่ภายใต้การทดสอบรวมถึงพื้นที่รัฐของส่วนประกอบหรือ mocks ในนั้น เว้นเสียแต่ว่าคุณจะมีพื้นที่ จำกัด มากการทดสอบไม่สามารถครอบคลุมพื้นที่รัฐนั้นได้ ความครอบคลุมที่สมบูรณ์จะเป็นข้อพิสูจน์ แต่จะใช้ได้เฉพาะกับพื้นที่เล็ก ๆ ของรัฐเท่านั้นเช่นการทดสอบวัตถุเดี่ยวที่มีไบต์ที่ไม่แน่นอนเดียวหรือจำนวนเต็ม 16 บิต การพิสูจน์อัตโนมัติมีค่ามากกว่าอย่างมากมาย
Frank Hileman

1
@cmaster คุณสรุปความแตกต่างระหว่างการทดสอบและการพิสูจน์ได้เป็นอย่างดี ขอบคุณ!
Frank Hileman

2

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

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

นอกจากนี้ฉันยินดีมากกว่าที่จะยอมรับผลข้างเคียงตราบใดที่ฉันสามารถย้อนกลับได้เช่นถ้าวิธีการของฉันแก้ไขข้อมูลใน db ให้มัน! ตราบใดที่ฉันสามารถย้อนกลับ db ไปยังสถานะก่อนหน้านี้อันตรายคืออะไร? นอกจากนี้ยังมีประโยชน์ที่การทดสอบของฉันสามารถตรวจสอบว่าข้อมูลดูตามที่คาดไว้หรือไม่ ฐานข้อมูลในหน่วยความจำหรือรุ่นทดสอบเฉพาะของ dbs ช่วยได้จริงๆที่นี่ (เช่นรุ่นทดสอบในหน่วยความจำ RavenDBs)

ในที่สุดฉันชอบที่จะเยาะเย้ยในขอบเขตการให้บริการเช่นอย่าทำการเรียก http ไปยังบริการ b แต่ลองมาสกัดกั้นและแนะนำแอพที่เหมาะสม


1

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

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

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

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

ดังนั้นจะทำอย่างไรเมื่อตัวแบ่งรหัส ถ้ามันได้รับการเข้ารหัสไปยังอินเทอร์เฟซแล้วเพียงแค่การตัดสินใจที่จำเป็นต้องเปลี่ยน (พร้อมกับการทดสอบใด ๆ )

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

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


ที่คุณมีนิ่วผมจะได้เขียนการดำเนินงาน นี่คือ calque จากภาษาอื่น (ฉันจะเดาภาษาฝรั่งเศส) หรือมีความแตกต่างที่สำคัญระหว่างการสรุปและการดำเนินการหรือไม่
Peter Taylor

0

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

เมื่อคุณเขียนการทดสอบหน่วยสำหรับ A คุณจำลอง X ในคำอื่น ๆ ในขณะที่การทดสอบหน่วย A คุณตั้งค่า (สมมุติ) พฤติกรรมของจำลองของ X ให้เป็น X1

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

ตัวอย่างเช่นสมมติว่า A เป็นอัลกอริธึมการเรียงลำดับและ A จัดเตรียมข้อมูลที่จะเรียงลำดับ mock of X ควรให้ค่าที่ส่งคืนเป็นโมฆะ, รายการว่างของข้อมูล, องค์ประกอบเดียว, องค์ประกอบหลายรายการที่เรียงลำดับแล้ว, องค์ประกอบหลายรายการที่ยังไม่ได้จัดเรียง, องค์ประกอบหลายรายการเรียงลำดับย้อนหลัง, รายการที่มีองค์ประกอบเดียวกันซ้ำแล้วซ้ำ ๆ องค์ประกอบจำนวนมากและควรมีข้อยกเว้น

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


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

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

จะเกิดอะไรขึ้นถ้าฉันใช้อาร์เรย์ใน PHP หรือใน Java หรือใน Javascript ถ้าคุณเปลี่ยนดัชนีของอาร์เรย์หรือลบออกไม่ใช่คอมไพเลอร์ภาษาเหล่านั้นจะบอกคุณเกี่ยวกับมันดัชนีอาจซ้อนอยู่ในอันดับที่ 36 ระดับที่ซ้อนกันของอาร์เรย์ดังนั้นฉันคิดว่าคอมไพเลอร์ไม่ใช่วิธีแก้ปัญหาสำหรับเรื่องนี้
FantomX1

-2

คุณต้องดูการทดสอบที่แตกต่างกัน

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

A น่าจะเป็นการเยาะเย้ย X สำหรับการทดสอบหน่วยและการทดสอบกับ Mock ควรผ่านไปเรื่อย ๆ แม้หลังจากที่คุณเปลี่ยนแล้ว

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

นอกจากนี้หาก X ต้องมีพฤติกรรมใหม่จะเป็นการดีกว่าถ้าจะให้วิธีการใหม่ที่จะให้ผลลัพธ์ที่ต้องการ

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