如何模拟随机.python中的选择



我希望choice在单元测试中每次都返回相同的值1000。下面的代码不起作用

import unittest
from random import choice
from mock import mock
def a():
    return choice([1, 2, 3])
class mockobj(object):
    @classmethod
    def choice(cls, li):
        return 1000
class testMock(unittest.TestCase):
    def test1(self):
        with mock.patch('random.choice', mockobj.choice):
            self.assertEqual(a(), 1000)

错误信息如下:

Failure
Traceback (most recent call last):
  File "test.py", line 15, in test1
    self.assertEqual(a(), 1000)
AssertionError: 3 != 1000

我应该如何修改它使它工作?我使用python2.7

这里的问题是a()使用的是未打补丁的版本的random.choice

比较ab函数:

import random
from random import choice
def a():
    return choice([1, 2, 3])
def b():
    return random.choice([1, 2, 3])
def choice1000(values):
    return 1000
import unittest.mock as mock
with mock.patch('random.choice', choice1000):
    print('a', a())
    print('b', b())

打印例如:

a 3
b 1000
<标题>为什么?

这一行就是问题所在:

from random import choice

先导入random,然后将random.choice存储到名为choice的新变量中。

后来,mock.patch修补了原来的random.choice,但没有修补本地的choice

可以打本地补丁吗?是的:

with mock.patch('__main__.choice', choice1000):
    print('a', a())
    print('b', b())

现在它打印例如

a 1000
b 1

(我使用'__main__',因为我把这段代码放在顶层文件中——在您的情况下,它可能是别的东西)

那么该怎么办呢?

要么修补一切,要么采取不同的方法。例如,将choice()替换为a()

<标题> 替代解决方案

在这种情况下,如果您想测试random函数的行为,最好使用种子

def a():
    return random.choice([1, 2, 3, 1000])
def test1(self):
    random.seed(0)
    self.assertEqual(a(), 1000)

你不能事先知道某个种子会生成什么随机值,但你可以确定它们总是相同的。这正是你在测试中需要的。

在上面的最后一个例子中,我在random.seed(0)之后测试了a()一次,它返回了1000,所以我可以肯定它每次都会这样做:

>>> import random
>>> random.seed(0)
>>> print (random.choice([1, 2, 3, 1000]))
1000
>>> random.seed(0)
>>> print (random.choice([1, 2, 3, 1000]))
1000
>>> random.seed(0)
>>> print (random.choice([1, 2, 3, 1000]))
1000
>>> random.seed(0)
>>> print (random.choice([1, 2, 3, 1000]))
1000

我不知道什么是mockobj从测试,但你可以做的是。

    @mock.patch('random.choice')
    def test1(self, choice_mock):
        choice_mock.return_value = 1000
        self.assertEqual(a(), 1000)

我想用一个完整的脚本来改进@Alex响应,以便更好地理解和适应其他情况。

import random
from unittest import TestCase, mock
letters = ['A', 'B', 'C', 'D']
def get_random_words(): # Simple function using choice
  l = []
  for _ in range(3):
      l.append(random.choice(letters))
    
  return "".join(l)
class TestRandom(TestCase):
   @mock.patch('random.choice') # *(1)
   def test_get_random_words(self, mock_choice):
    
      mock_choice.side_effect = ['A','b','D','Z'] # *(2)
      result = get_random_words()
    
      self.assertEqual(result, 'AbD', 'Does not generate correct string')
<标题> 考虑

*(1)在本例中,该函数位于同一文件中,但如果它位于另一个文件中,则必须更改补丁的路径例:@mock.patch('your_package.your_file.your_function.random.choice')

*(2)在这种情况下,get_random_words函数调用random.choice 3次。这就是为什么必须在mock_choice.side_effect中放入相等或更多的项。这是因为如果它有更少的项目,它将抛出StopIteration错误。

最新更新