Python จำลองค่าที่ส่งคืนหลายค่า


169

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

นี่คือรหัสของฉันสำหรับการอ้างอิง

def get_boolean_response():
    response = io.prompt('y/n').lower()
    while response not in ('y', 'n', 'yes', 'no'):
        io.echo('Not a valid input. Try again'])
        response = io.prompt('y/n').lower()

    return response in ('y', 'yes')

รหัสทดสอบของฉัน:

@mock.patch('io')
def test_get_boolean_response(self, mock_io):
    #setup
    mock_io.prompt.return_value = ['x','y']
    result = operations.get_boolean_response()

    #test
    self.assertTrue(result)
    self.assertEqual(mock_io.prompt.call_count, 2)

io.promptเป็นเพียง "อิสระ" ของแพลตฟอร์ม (python 2 และ 3) ดังนั้นในที่สุดฉันพยายามเยาะเย้ยการป้อนข้อมูลของผู้ใช้ ฉันได้ลองใช้รายการสำหรับค่าส่งคืนแล้ว แต่นั่นก็ไม่ได้ทำงานได้ดี

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

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


ไม่ใช่ความซ้ำซ้อนของคำถามนี้ส่วนใหญ่เพราะฉันไม่มีความสามารถในการเปลี่ยนแปลงอินพุต

หนึ่งในความคิดเห็นของคำตอบสำหรับคำถามนี้อยู่ในบรรทัดเดียวกัน แต่ไม่มีการให้คำตอบ / ความคิดเห็น


3
response is not 'y' or 'n' or 'yes' or 'no'ในการไม่ทำสิ่งที่คุณคิด ดูฉันจะทดสอบตัวแปรหนึ่งตัวกับหลายค่าได้อย่างไร และคุณไม่ควรใช้isเพื่อเปรียบเทียบค่าสตริงใช้==เพื่อเปรียบเทียบค่าไม่ใช่ตัวตนของวัตถุ
Martijn Pieters

ระวังที่นี่ด้วย ดูเหมือนว่าคุณกำลังพยายามใช้isเพื่อเปรียบเทียบตัวอักษรสตริง อย่าทำอย่างนั้น ความจริงที่ว่ามันใช้งานได้ (บางครั้ง) เป็นเพียงรายละเอียดการใช้งานใน CPython นอกจากนี้response is not 'y' or 'n' or 'yes' or 'no'อาจจะไม่ทำในสิ่งที่คุณคิดว่ามันคือ ...
mgilson

คำตอบ:


301

คุณสามารถกำหนดiterableไปside_effectและเยาะเย้ยจะกลับค่าในลำดับถัดไปในแต่ละครั้งจะเรียกว่า:

>>> from unittest.mock import Mock
>>> m = Mock()
>>> m.side_effect = ['foo', 'bar', 'baz']
>>> m()
'foo'
>>> m()
'bar'
>>> m()
'baz'

การอ้างถึงMock()เอกสาร :

ถ้าside_effectสามารถทำซ้ำได้การเรียกแต่ละครั้งไปที่ mock จะส่งคืนค่าถัดไปจาก iterable

นอกเหนือการทดสอบresponse is not 'y' or 'n' or 'yes' or 'no'จะไม่ทำงาน คุณกำลังถามว่าการแสดงออก(response is not 'y')เป็นความจริงหรือ'y'เป็นความจริง (เสมอกรณีสตริงไม่ว่างเปล่าอยู่เสมอจริง) ฯลฯ แสดงออกต่างๆบนด้านข้างของทั้งorผู้ประกอบการมีความเป็นอิสระ ดูฉันจะทดสอบตัวแปรหนึ่งตัวกับหลายค่าได้อย่างไร

คุณควรจะยังไม่ใช้isในการทดสอบกับสตริง CPython interpreter อาจใช้วัตถุสตริงอีกครั้งภายใต้สถานการณ์บางอย่างแต่นี่ไม่ใช่พฤติกรรมที่คุณควรวางใจ

เช่นนี้ใช้:

response not in ('y', 'n', 'yes', 'no')

แทน; นี้จะใช้การทดสอบความเท่าเทียมกัน ( ==) เพื่อตรวจสอบว่าresponseการอ้างอิงสตริงที่มีเนื้อหา (ค่าเดียวกัน)

เช่นเดียวกับresponse == 'y' or 'yes'; ใช้response in ('y', 'yes')แทน


มีวิธีทำกับมาตรฐานmockหรือไม่ มีวิธีใช้แพทช์กับ MagicMock เหมือนที่ฉันทำกับ mock มาตรฐานหรือไม่?
Nick Humrich

@Humdinger: นี่เป็นคุณลักษณะของMockคลาสstardard
Martijn Pieters

17
การกำหนดรายการดูเหมือนว่าจะทำงานกับหลาม 3 เท่านั้น การทดสอบกับ python 2.7 ฉันจำเป็นต้องใช้ตัววนซ้ำแทน ( m.side_effect = iter(['foo', 'bar', 'baz']))
user686249

1
@ user686249: ฉันแน่นอนสามารถทำซ้ำนี้เพราะ speccing จากวิธีการผลิตlambda(ฟังก์ชั่น) MagicMockไม่ได้เป็น วัตถุฟังก์ชั่นไม่สามารถมีคุณสมบัติดังนั้นside_effectแอตทริบิวต์มีจะเป็น iterable คุณไม่ควรระบุวิธีการเช่นนั้น ใช้ดีกว่าmock.patch.object(requests.Session, 'post'); ซึ่งส่งผลให้วัตถุ patcher ที่ระบุรายละเอียดโดยอัตโนมัติในวิธีการและสนับสนุนside_effectอย่างถูกต้อง
Martijn Pieters

3
@ JoeMjr2: เมื่อ iterator คือหมดStopIterationถูกยกขึ้น คุณสามารถใช้ iterator ใด ๆ เพื่อให้คุณสามารถใช้itertools.chain(['Foo'], itertools.repeat('Bar'))ในการผลิตครั้งแล้วตลอดไปผลิตFoo Bar
Martijn Pieters
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.