การทดสอบหน่วยที่ดีเพื่อครอบคลุมกรณีการใช้งานของการกลิ้งแม่พิมพ์


18

ฉันพยายามที่จะจับกับการทดสอบหน่วย

สมมติว่าเรามีแม่พิมพ์ซึ่งสามารถมีจำนวนด้านเริ่มต้นเท่ากับ 6 (แต่สามารถมี 4, 5 ด้าน ฯลฯ ):

import random
class Die():
    def __init__(self, sides=6):
        self._sides = sides

    def roll(self):
        return random.randint(1, self._sides)

ต่อไปนี้เป็นการทดสอบที่ถูกต้อง / มีประโยชน์หรือไม่

  • ทดสอบม้วนในช่วง 1-6 สำหรับแม่พิมพ์แบบ 6 ด้าน
  • ทดสอบม้วน 0 สำหรับแม่พิมพ์แบบ 6 ด้าน
  • ทดสอบม้วน 7 สำหรับแม่พิมพ์แบบ 6 ด้าน
  • ทดสอบม้วนในช่วง 1-3 สำหรับแม่พิมพ์ 3 ด้าน
  • ทดสอบม้วน 0 สำหรับแม่พิมพ์ 3 ด้าน
  • ทดสอบม้วน 4 สำหรับแม่พิมพ์ 3 ด้าน

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

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


1
สิ่งที่เกี่ยวกับการตายลบ 5 ด้านหรือตายด้าน null?
JensG

คำตอบ:


22

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

เป็นไปได้อย่างแน่นอนว่ารหัสของคุณใช้random.randint()ผิด หรือคุณจะโทรมาrandom.randrange(1, self._sides)แทนและความตายของคุณจะไม่ส่งผ่านค่าสูงสุด แต่นั่นเป็นข้อผิดพลาดประเภทอื่นไม่ใช่คนที่คุณสามารถจับได้ด้วยความไม่สุภาพ ในกรณีดังกล่าวdie หน่วยของคุณทำงานตามที่ได้ออกแบบไว้ แต่การออกแบบนั้นมีข้อบกพร่อง

ในกรณีนี้ฉันต้องการใช้เยาะเย้ยเพื่อแทนที่randint()ฟังก์ชั่นและมีเพียงตรวจสอบว่าได้รับการเรียกว่าได้อย่างถูกต้อง Python 3.3 ขึ้นไปมาพร้อมกับunittest.mockโมดูลเพื่อจัดการกับการทดสอบประเภทนี้ แต่คุณสามารถติดตั้งmockแพ็กเกจภายนอกในเวอร์ชั่นเก่ากว่าเพื่อรับฟังก์ชั่นที่แน่นอน

import unittest
try:
    from unittest.mock import patch
except ImportError:
    # < python 3.3
    from mock import patch


@patch('random.randint', return_value=3)
class TestDice(unittest.TestCase):
    def _make_one(self, *args, **kw):
        from die import Die
        return Die(*args, **kw)

    def test_standard_size(self, mocked_randint):
        die = self._make_one()
        result = die.roll()

        mocked_randint.assert_called_with(1, 6)
        self.assertEqual(result, 3)

    def test_custom_size(self, mocked_randint):
        die = self._make_one(sides=42)
        result = die.roll()

        mocked_randint.assert_called_with(1, 42)
        self.assertEqual(result, 3)


if __name__ == '__main__':
    unittest.main()

ด้วยการล้อเลียนการทดสอบของคุณก็ง่ายมาก มีเพียง 2 รายเท่านั้นจริงๆ กรณีเริ่มต้นสำหรับตาย 6 ด้านและกรณีด้านที่กำหนดเอง

มีวิธีอื่นในการแทนที่randint()ฟังก์ชันในเนมสเปซส่วนกลางของชั่วคราวDieแต่mockโมดูลทำให้สิ่งนี้ง่ายที่สุด @mock.patchมัณฑนากรที่นี่นำไปใช้ทุกวิธีการทดสอบในกรณีการทดสอบ; แต่ละวิธีการทดสอบจะถูกส่งผ่านอาร์กิวเมนต์พิเศษrandom.randint()ฟังก์ชั่นที่เยาะเย้ยดังนั้นเราสามารถทดสอบกับจำลองเพื่อดูว่ามันถูกเรียกอย่างถูกต้องหรือไม่ return_valueอาร์กิวเมนต์ระบุสิ่งที่กลับมาจากการเยาะเย้ยเมื่อมันถูกเรียกว่าเพื่อให้เราสามารถตรวจสอบว่าdie.roll()วิธีการที่แน่นอนกลับ 'สุ่ม' ผลให้เรา

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

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

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

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

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


1
คุณมีกรณีทดสอบมากกว่า 2 กรณี ... ผลการตรวจสอบค่าเริ่มต้น: ต่ำกว่า (1), สูงกว่า (6), ต่ำกว่า (0), ต่ำกว่า (7) และผลลัพธ์สำหรับตัวเลขที่ผู้ใช้ระบุเช่น max_int การป้อนข้อมูลอื่น ๆ ยังไม่ได้ตรวจสอบซึ่งอาจต้องมีการทดสอบสำหรับในบางจุด ...
เจมส์ปราดเปรื่อง

2
ไม่มีผู้ที่มีการทดสอบไม่ได้รหัสในrandint() Die.roll()
Martijn Pieters

มีวิธีการเพื่อให้แน่ใจว่าไม่เพียง แต่เรียกใช้ randint อย่างถูกต้อง แต่ผลลัพธ์นั้นถูกใช้อย่างถูกต้องด้วย: จำลองเพื่อคืนค่าsentinel.dieตัวอย่าง (วัตถุ sentinel มาจากunittest.mockเกินไป) จากนั้นตรวจสอบว่าเป็นสิ่งที่ถูกส่งกลับจากวิธีการม้วนของคุณ วิธีนี้ช่วยให้สามารถใช้วิธีทดสอบได้เพียงวิธีเดียวเท่านั้น
aragaer

@aragaer: แน่นอนถ้าคุณต้องการยืนยันว่าค่าที่ส่งคืนไม่เปลี่ยนแปลงsentinel.dieจะเป็นวิธีที่ดีในการตรวจสอบ
Martijn Pieters

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

16

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

คุณสามารถเยาะเย้ยrandint? ใช่คุณสามารถ. แต่คุณจะพิสูจน์อะไร ที่คุณเรียกมันว่ามีข้อโต้แย้ง 1 และด้านข้าง อะไรที่หมายถึง? คุณกลับมาที่จตุรัสหนึ่ง - ในตอนท้ายของวันคุณจะต้องพิสูจน์ - อย่างเป็นทางการหรือไม่เป็นทางการ - การโทรrandom.randint(1, sides)อย่างถูกต้องนั้นจะทำการทอยลูกเต๋า

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


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

@MartijnPieters ฉันไม่เห็นด้วยว่า "นั่นคือสิ่งที่การทดสอบการรวมสำหรับ" การทดสอบการรวมเพื่อตรวจสอบว่าองค์ประกอบทั้งหมดของระบบมีการโต้ตอบอย่างถูกต้อง พวกเขาไม่ใช่สถานที่ที่จะทดสอบว่าองค์ประกอบที่กำหนดให้ผลลัพธ์ที่ถูกต้องสำหรับอินพุตที่กำหนด สำหรับการทดสอบหน่วยกล่องดำกับกล่องขาวการทดสอบหน่วยกล่องขาวในที่สุดก็จะแตกกับการเปลี่ยนแปลงการใช้งานและสมมติฐานใด ๆ ที่คุณทำในการดำเนินการมีแนวโน้มที่จะดำเนินการในการทดสอบ การตรวจสอบความrandom.randintถูกต้องด้วยการเรียกใช้1, sidesนั้นไม่มีค่าหากเป็นสิ่งที่ผิดที่ต้องทำ
Doval

ใช่นั่นคือข้อ จำกัด ของการทดสอบหน่วยกล่องขาว อย่างไรก็ตามไม่มีจุดทดสอบที่random.randint()จะคืนค่าในช่วง [1, ด้าน] (รวม) randomได้อย่างถูกต้องซึ่งขึ้นอยู่กับนักพัฒนา Python เพื่อให้แน่ใจว่าหน่วยทำงานอย่างถูกต้อง
Martijn Pieters

และอย่างที่คุณพูดด้วยตัวคุณเองการทดสอบหน่วยไม่สามารถรับประกันได้ว่าโค้ดของคุณไม่มีข้อบกพร่อง หากรหัสของคุณใช้งานหน่วยอื่นอย่างไม่ถูกต้อง (พูดว่าคุณคาดหวังว่าrandom.randint()จะทำตัวเหมือนrandom.randrange()และเรียกมันว่าด้วยrandom.randint(1, sides + 1)คุณก็จะจมอยู่ดี
Martijn Pieters

2
@MartijnPieters ฉันเห็นด้วยกับคุณที่นั่น แต่นั่นไม่ใช่สิ่งที่ฉันคัดค้าน ผมคัดค้านการทดสอบ random.randint ที่จะถูกเรียกว่ามีข้อโต้แย้ง (1, ด้าน) คุณได้สันนิษฐานในการนำไปปฏิบัติว่านี่คือสิ่งที่ถูกต้องที่จะทำและตอนนี้คุณกำลังทำซ้ำข้อสันนิษฐานในการทดสอบ หากสมมติฐานนั้นผิดการทดสอบจะผ่านไป แต่การใช้งานของคุณยังไม่ถูกต้อง มันเป็นข้อพิสูจน์ครึ่งทางที่เต็มไปด้วยความเจ็บปวดในการเขียนและบำรุงรักษา
Doval

6

แก้ไขเมล็ดแบบสุ่ม สำหรับลูกเต๋าที่ 1, 2, 5 และ 12 ให้ยืนยันว่าสองสามพันม้วนให้ผลลัพธ์รวมถึง 1 และ N และไม่รวม 0 หรือ N + 1 หากดูจากโอกาสที่ประหลาดคุณจะได้รับผลการสุ่มที่ไม่มี ครอบคลุมช่วงที่คาดหมายสลับไปยังเมล็ดพันธุ์อื่น

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

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


3

คืออะไรDieถ้าคุณคิดเกี่ยวกับมันได้หรือไม่ - randomไม่เกินรอบเสื้อคลุม มันห่อหุ้มrandom.randintและบรรจุซ้ำในแง่ของคำศัพท์ของแอปพลิเคชันของคุณ: Die.Roll.

ฉันไม่พบว่าเกี่ยวข้องกับการแทรกเลเยอร์สิ่งที่เป็นนามธรรมระหว่างDieและrandomเนื่องจากDieตัวมันเองเป็นเลเยอร์ทางอ้อมระหว่างแอปพลิเคชันของคุณและแพลตฟอร์มแล้ว

หากคุณต้องการกระป๋องลูกเต๋าผลเพียงการเยาะเย้ยDierandomไม่เยาะเย้ย

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

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


2

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

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

ถ้าเป็นฉันฉันจะวิ่งพูด 6000 ม้วนนับพวกเขาและตรวจสอบให้แน่ใจว่าแต่ละหมายเลขจาก 1-6 ถูกหมุนระหว่าง 500 ถึง 1,500 ครั้ง ฉันจะตรวจสอบด้วยว่าไม่มีการส่งคืนตัวเลขที่อยู่นอกช่วงนั้น ฉันอาจตรวจสอบว่าสำหรับม้วนที่สองจำนวน 6,000 ม้วนเมื่อสั่ง [1..6] ตามลำดับความถี่ผลลัพธ์ที่ได้จะแตกต่างกัน หากคุณต้องการละเอียดถี่ถ้วนคุณอาจพบความถี่ของตัวเลขที่ตามหลัง 1, ตามด้วย 2 และอื่น ๆ ; แต่ให้แน่ใจว่าขนาดตัวอย่างของคุณใหญ่พอและคุณมีความแปรปรวนเพียงพอ มนุษย์คาดว่าตัวเลขสุ่มจะมีรูปแบบน้อยกว่าที่เป็นจริง

ทำซ้ำ 12 ด้านและ 2 ด้าน (6 ใช้มากที่สุดดังนั้นจึงเป็นสิ่งที่คาดหวังมากที่สุดสำหรับทุกคนที่เขียนรหัสนี้)

ในที่สุดฉันจะทดสอบเพื่อดูว่าเกิดอะไรขึ้นกับการตายแบบ 1 ด้าน, การตายแบบ 0 ด้าน, การตาย 1 ด้าน, การตาย 2.3 ด้าน, การตายด้าน 1 [1,2,3,4,5,6] และ "blah" ตายด้าน แน่นอนสิ่งเหล่านี้ควรล้มเหลว พวกเขาล้มเหลวในวิธีที่มีประโยชน์หรือไม่ สิ่งเหล่านี้น่าจะล้มเหลวในการสร้างไม่ใช่การกลิ้ง

หรือบางทีคุณอาจต้องการจัดการกับสิ่งต่าง ๆ เหล่านี้ด้วย - อาจสร้างตายด้วย [1,2,3,4,5,6] ควรเป็นที่ยอมรับ - และอาจเป็น "blah" เช่นกัน นี่อาจเป็นการตายที่มี 4 หน้าและแต่ละหน้ามีตัวอักษรอยู่ เกม "เกรงกลัว" สปริงใจเช่นเดียวกับลูกมายากลแปด

และสุดท้ายคุณอาจต้องการพิจารณาสิ่งนี้: http://lh6.ggpht.com/-fAGXwbJbYRM/UJA_31ACOLI/AAAAAAAAAPg/2FxOWzo96KE/s1600-h/random%25255B3%255D.jpg


2

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

กลยุทธ์ของฉันคือการล้อเลียน RNG ด้วยสิ่งที่สร้างกระแสค่าที่คาดการณ์ได้ซึ่งครอบคลุมพื้นที่ทั้งหมด ถ้า (พูด) side = 6 และ RNG สร้างค่าจาก 0 ถึง 5 ตามลำดับฉันสามารถทำนายได้ว่าคลาสของฉันควรทำอย่างไรและทดสอบหน่วยตาม

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

มันง่ายกำหนดได้ทำซ้ำและจับข้อบกพร่องได้ ฉันจะใช้กลยุทธ์เดิมอีกครั้ง


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


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

@bdrx: นี่เป็นเมื่อเร็ว ๆ นี้: ฉันจะตอบมันต่างไปก่อน แต่ดูแก้ไข
david.pfx

1

การทดสอบที่คุณแนะนำในคำถามของคุณไม่ตรวจจับตัวนับเลขคณิตแบบแยกส่วนเป็นการใช้งาน return 1 + (random.randint(1,maxint) % sides)และพวกเขาไม่ตรวจสอบข้อผิดพลาดของการดำเนินงานร่วมกันในการกระจายความน่าจะเป็นที่เกี่ยวข้องกับรหัสเช่น หรือการเปลี่ยนแปลงตัวกำเนิดที่ส่งผลให้มีรูปแบบสองมิติ

หากคุณต้องการยืนยันว่าคุณกำลังสร้างตัวเลขสุ่มปรากฏที่กระจายอย่างสม่ำเสมอคุณต้องตรวจสอบคุณสมบัติที่หลากหลาย หากต้องการทำงานให้ดีพอสมควรคุณสามารถเรียกใช้http://www.phy.duke.edu/~rgb/General/dieharder.phpจากหมายเลขที่คุณสร้างขึ้น หรือเขียนชุดทดสอบหน่วยที่ซับซ้อนในทำนองเดียวกัน

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


-1

การทดสอบที่ง่ายที่สุดของ die-roll คือการทำซ้ำหลายแสนครั้งและตรวจสอบว่าผลลัพธ์ที่เป็นไปได้แต่ละครั้งมีการเข้าชมโดยประมาณ (1 / จำนวนด้าน) ในกรณีของการตายแบบ 6 ด้านคุณควรเห็นค่าที่เป็นไปได้ในแต่ละครั้งที่มีค่าประมาณ 16.6% ของเวลา หากมีการปิดมากกว่าร้อยละแล้วคุณมีปัญหา

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


1
การทดสอบนี้จะผ่านการใช้งานที่ไม่สุ่มทั้งหมดซึ่งวนซ้ำไปเรื่อย ๆ ตามลำดับที่กำหนดไว้ล่วงหน้า
ริ้น

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