สามารถใช้เลขอาถรรพ์ในการทดสอบหน่วยได้หรือไม่หากตัวเลขไม่ได้มีความหมายอะไรเลย?


59

ในการทดสอบหน่วยของฉันฉันมักจะโยนค่าโดยพลการที่รหัสของฉันเพื่อดูว่ามันทำอะไร ตัวอย่างเช่นถ้าฉันรู้ว่าfoo(1, 2, 3)ควรจะคืน 17 ฉันอาจเขียนนี้:

assertEqual(foo(1, 2, 3), 17)

ตัวเลขเหล่านี้เป็นกฎเกณฑ์โดยธรรมชาติและไม่มีความหมายที่กว้างกว่า (ตัวอย่างเช่นมันไม่ใช่เงื่อนไขขอบเขตแม้ว่าฉันจะทำการทดสอบกับสิ่งเหล่านั้นเช่นกัน) ฉันจะพยายามหาชื่อที่ดีสำหรับตัวเลขเหล่านี้และการเขียนอะไรทำนองconst int TWO = 2;นั้นก็ไม่ได้ช่วยอะไรเลย มันโอเคที่จะเขียนแบบทดสอบนี้หรือฉันควรแยกตัวเลขออกเป็นค่าคงที่หรือไม่?

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


9
หากคุณใส่ค่าลงไปและคาดว่าจะสามารถอ่านค่าเดิมเหล่านั้นกลับคืนมาได้ฉันจะบอกว่าตัวเลขเวทย์มนตร์นั้นดี ดังนั้นถ้าสมมุติว่า1, 2, 3เป็นดัชนีอาร์เรย์ 3 มิติที่คุณเก็บค่าไว้ก่อนหน้านี้17ฉันคิดว่าการทดสอบนี้จะดูดี (ตราบใดที่คุณมีการทดสอบเชิงลบด้วย) แต่ถ้าเป็นผลจากการคำนวณคุณควรตรวจสอบให้แน่ใจว่าทุกคนที่อ่านการทดสอบนี้จะเข้าใจว่าทำไมfoo(1, 2, 3)ต้องเป็น17เช่นนั้นและตัวเลขเวทมนต์อาจไม่บรรลุเป้าหมาย
Joe White

24
const int TWO = 2;2แม้จะเลวร้ายยิ่งกว่าเพียงแค่ใช้ มันสอดคล้องกับถ้อยคำของกฎโดยมีเจตนาที่จะละเมิดจิตวิญญาณของมัน
Agent_L

4
ตัวเลขที่ "ไม่ได้หมายถึงอะไร" คืออะไร? ทำไมมันจะอยู่ในรหัสของคุณถ้ามันไม่มีความหมายอะไร?
ทิมแกรนท์

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

5
ตัวอย่างของคุณทำให้เข้าใจผิด - เมื่อชื่อฟังก์ชั่นของคุณเป็นจริงfooมันจะไม่หมายถึงอะไรเลยและพารามิเตอร์ แต่ในความเป็นจริงผมค่อนข้างแน่ใจว่าฟังก์ชั่นไม่ได้มีชื่อและพารามิเตอร์ที่ไม่ได้มีชื่อbar1, และbar2 bar3ทำให้เป็นจริงมากขึ้นตัวอย่างที่ชื่อมี ความหมายจากนั้นมันก็สมเหตุสมผลมากกว่าที่จะอภิปรายว่าค่าข้อมูลการทดสอบนั้นต้องการชื่อด้วยเช่นกัน
Doc Brown

คำตอบ:


81

เมื่อไหร่ที่คุณมีตัวเลขที่ไม่มีความหมายเลย?

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

ตัวอย่าง:

const int startBalance = 10000;
const float interestRate = 0.05f;
const int years = 5;

const int expectedEndBalance = 12840;

assertEqual(calculateCompoundInterest(startBalance, interestRate, years),
            expectedEndBalance);

โปรดทราบว่าตัวแปรแรกไม่ได้ถูกตั้งชื่อHUNDRED_DOLLARS_ZERO_CENTแต่startBalanceเพื่อแสดงว่าอะไรคือความหมายของตัวแปร แต่ไม่ใช่ว่าค่าของมันนั้นมีความพิเศษ แต่อย่างใด


3
@ เควิน - คุณกำลังทดสอบภาษาอะไร กรอบการทดสอบบางอย่างช่วยให้คุณสามารถตั้งค่าผู้ให้บริการข้อมูลซึ่งส่งคืนอาร์เรย์ของค่าสำหรับการทดสอบได้
HorusKol

10
แม้ว่าฉันจะเห็นด้วยกับความคิด แต่ระวังว่าการปฏิบัตินี้สามารถนำข้อผิดพลาดใหม่เช่นกันเช่นถ้าคุณดึงค่าเช่น0.05fการintตั้งใจ :)
เจฟฟ์โบว์แมน

5
+1 - สิ่งดีๆ เพียงเพราะคุณไม่สนใจสิ่งที่มีค่าโดยเฉพาะอย่างยิ่งก็คือว่าไม่ได้หมายความว่ามันไม่ได้ยังคงมีจำนวนมายากล ...
ร็อบบี้ดี

2
@PieterB: AFAIK เป็นความผิดของ C และ C ++ ซึ่งทำให้ความคิดของconstตัวแปรเป็นระเบียบ
Steve Jessop

2
คุณตั้งชื่อตัวแปรของคุณเหมือนกับพารามิเตอร์ที่ตั้งชื่อcalculateCompoundInterestหรือไม่? ถ้าเป็นเช่นนั้นการพิมพ์พิเศษเป็นหลักฐานการทำงานที่คุณได้อ่านเอกสารสำหรับฟังก์ชั่นที่คุณกำลังทดสอบหรือคัดลอกชื่ออย่างน้อยที่ IDE ของคุณมอบให้ ฉันไม่แน่ใจว่าสิ่งนี้บอกผู้อ่านเกี่ยวกับจุดประสงค์ของรหัส แต่ถ้าคุณส่งพารามิเตอร์ในลำดับที่ไม่ถูกต้องอย่างน้อยพวกเขาก็สามารถบอกได้ว่าสิ่งใดตั้งใจ
Steve Jessop

20

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

ยกตัวอย่างเช่นสมมติฐานเป็นห้องสมุดหลามเย็นสำหรับการจัดเรียงของการทดสอบนี้และจะขึ้นอยู่กับQuickCheck

คิดว่าการทดสอบหน่วยปกติเป็นเหมือนดังต่อไปนี้:

  1. ตั้งค่าข้อมูลบางอย่าง
  2. ดำเนินการบางอย่างกับข้อมูล
  3. ยืนยันบางสิ่งเกี่ยวกับผลลัพธ์

สมมติฐานช่วยให้คุณเขียนแบบทดสอบซึ่งมีลักษณะดังนี้:

  1. สำหรับข้อมูลทั้งหมดที่ตรงกับข้อกำหนดบางอย่าง
  2. ดำเนินการบางอย่างกับข้อมูล
  3. ยืนยันบางสิ่งเกี่ยวกับผลลัพธ์

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

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

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


11
การทดสอบแบบสุ่มอาจพบข้อบกพร่องที่ยากที่จะทำซ้ำ แต่การทดสอบแบบสุ่มหาข้อบกพร่องที่ทำซ้ำได้ยาก ตรวจสอบให้แน่ใจว่าได้จับการทดสอบที่ล้มเหลวด้วยเคสทดสอบที่ทำซ้ำได้เฉพาะ
JBRWilkinson

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

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

7
@NajibIdrissi - ไม่จำเป็น ตัวอย่างเช่นคุณสามารถทดสอบว่าการใช้ค่าผกผันของการดำเนินการที่คุณกำลังทดสอบกับผลลัพธ์จะให้ค่าเริ่มต้นที่คุณได้รับกลับมา หรือคุณสามารถทดสอบค่าคงที่ที่คาดหวัง (เช่นสำหรับการคำนวณดอกเบี้ยทุกdวันการคำนวณที่dวัน + 1 เดือนควรเป็นอัตราเปอร์เซ็นต์รายเดือนที่สูงขึ้น) ฯลฯ
Jules

12
@Chris - ในหลาย ๆ กรณีการตรวจสอบผลลัพธ์นั้นถูกต้องนั้นง่ายกว่าการสร้างผลลัพธ์ แม้ว่าสิ่งนี้จะไม่เป็นจริงในทุกสถานการณ์ แต่ก็มีหลายอย่างที่เป็นอยู่ ตัวอย่าง: การเพิ่มรายการลงในต้นไม้ไบนารีแบบสมดุลควรส่งผลให้ต้นไม้ใหม่ที่มีความสมดุล ... ง่ายต่อการทดสอบค่อนข้างยุ่งยากในการใช้ในทางปฏิบัติ
จูลส์

11

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

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

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

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


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

@Robbie ดูชื่อ / เอกสารสำหรับการทดสอบควรจะให้บริบทที่เหมาะสมและคำอธิบายของตัวเลขมายากลใด ๆ ที่มีอยู่ในการทดสอบ หากไม่มีให้เพิ่มเอกสารหรือเปลี่ยนชื่อการทดสอบเพื่อให้ชัดเจนยิ่งขึ้น
enderland

มันจะเป็นการดีกว่าถ้าจะให้ชื่อหมายเลขเวทย์มนตร์ หากจำนวนของพารามิเตอร์มีการเปลี่ยนแปลงเอกสารความเสี่ยงที่ล้าสมัย
Robbie Dee

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

"หวังว่า" ฮะ? ทำไมไม่เพียงรหัสสิ่งที่ถูกต้องและทำไปด้วยสิ่งที่เป็นประหนึ่งจำนวนมายากลเป็นฟิลิปป์ได้ระบุไว้แล้ว ...
ร็อบบี้ดี

9

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

// standard triangle with area >0
assertEqual(testForTriangle(2, 3, 4), true);

// degenerated triangle, length of two edges match the length of the third
assertEqual(testForTriangle(1, 2, 3), true);  

// no triangle
assertEqual(testForTriangle(1, 2, 4), false); 

// two sides equal
assertEqual(testForTriangle(2, 2, 3), true);

// all three sides equal
assertEqual(testForTriangle(4, 4, 4), true);

// degenerated triangle / point
assertEqual(testForTriangle(0, 0, 0), true);  

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

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


ทางออกที่เหมาะสม
user1725145

6

ทำไมเราต้องการใช้ชื่อคงที่แทนตัวเลข?

  1. DRY - หากฉันต้องการค่าที่ 3 แห่งฉันต้องการกำหนดเพียงครั้งเดียวดังนั้นฉันสามารถเปลี่ยนได้ในที่เดียวหากเปลี่ยน
  2. ให้ความหมายกับตัวเลข

หากคุณเขียนการทดสอบหลายหน่วยแต่ละการทดสอบมี 3 ตัวเลข (startBalance, ดอกเบี้ย, ปี) - ฉันเพิ่งจะแพ็คค่าลงในการทดสอบหน่วยเป็นตัวแปรท้องถิ่น ขอบเขตที่เล็กที่สุดที่เป็นของมัน

testBigInterest()
  var startBalance = 10;
  var interestInPercent = 100
  var years = 2
  assert( calcCreditSum( startBalance, interestInPercent, years ) == 40 )

testSmallInterest()
  var startBalance = 50;
  var interestInPercent = .5
  var years = 1
  assert( calcCreditSum( startBalance, interestInPercent, years ) == 50.25 )

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

testBigInterest()
  assert( calcCreditSum( startBalance:       10
                        ,interestInPercent: 100
                        ,years:               2 ) = 40 )

หรือใช้การทดสอบ Framework ซึ่งจะช่วยให้คุณกำหนดกรณีทดสอบในบางอาร์เรย์หรือรูปแบบแผนที่:

testcases = { {
                Name: "BigInterest"
               ,StartBalance:       10
               ,InterestInPercent: 100
               ,Years:               2
              }
             ,{ 
                Name: "SmallInterest"
               ,StartBalance:       50
               ,InterestInPercent:  .5
               ,Years:               1
              }
            }

3

... แต่ในกรณีนี้ตัวเลขไม่มีความหมายเลย

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


1
สิ่งนี้ไม่เป็นความจริง แต่อย่างในตัวอย่างของการทดสอบหน่วยล่าสุดที่ฉันเขียน ( assertEqual "Returned value" (makeKindInt 42) (runTest "lvalue_operators")) ในตัวอย่างนี้42เป็นเพียงค่าตัวยึดตำแหน่งที่สร้างโดยรหัสในสคริปต์ทดสอบที่มีชื่อlvalue_operatorsและตรวจสอบแล้วเมื่อมีการส่งคืนโดยสคริปต์ มันไม่มีความหมายเลยนอกจากค่าเดียวกันจะเกิดขึ้นในสองแห่ง ชื่อที่เหมาะสมที่นี่ที่จริงให้ความหมายที่เป็นประโยชน์อะไร
จูลส์

3

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

struct test_foo_values {
    int bar;
    int baz;
    int blurf;
    int expected;
};
const struct test_foo_values test_foo_with[] = {
   { 1, 2, 3, 17 },
   { 2, 4, 9, 34 },
   // ... many more here ...
};

for (size_t i = 0; i < ARRAY_SIZE(test_foo_with); i++) {
    const struct test_foo_values *c = test_foo_with[i];
    assertEqual(foo(c->bar, c->baz, c->blurf), c->expected);
}

เครื่องมือเช่นที่แนะนำในคำตอบของ Dannnnoสามารถช่วยคุณสร้างตารางค่าที่จะทดสอบ bar, bazและblurfควรจะถูกแทนที่โดยชื่อที่มีความหมายตามที่กล่าวไว้ในคำตอบของฟิลิปป์

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


1

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

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

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


1

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

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

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

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

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

ในขณะที่เรา“ จำลอง” ฐานข้อมูลคุณสามารถเรียกการทดสอบเหล่านี้ว่า“ การทดสอบหน่วย” แต่“ หน่วย” นั้นค่อนข้างใหญ่

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


1

ฉันคิดว่าในกรณีนี้ตัวเลขควรจะเรียกว่า Arbitrary Numbers มากกว่า Magic Numbers และเพียงแค่แสดงความคิดเห็นบรรทัดเป็น "กรณีทดสอบโดยพลการ"

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


0

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

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

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

  3. เขียนหมายเลขที่2เป็นTWOอาจจะเป็นประโยชน์ในบริบทบางอย่างและไม่มากในบริบทอื่น ๆ

    • ตัวอย่างเช่น: assertEquals(TWO, half_of(FOUR))เหมาะสมกับคนที่อ่านรหัส เป็นที่ชัดเจนทันทีว่าคุณกำลังทดสอบอะไร
    • แต่ถ้าการทดสอบของคุณassertEquals(numCustomersInBank(BANK_1), TWO)แล้วนี้ไม่ได้ทำให้ที่รู้สึกมาก ทำไมไม่BANK_1ประกอบด้วยลูกค้าสอง? สิ่งที่เรากำลังทดสอบหา?
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.