如何在Python单元测试中模拟一个类?

时间:2015-01-21 18:56:02

标签: python testing mocking

我有一个班级:

class A:
    __init__(self):
        self.b = B()

   def is_authorized(self)
      name = self.b.get_name()

      if name == 'admin':
          return True
      else:
          return False

我想编写一个单元测试来测试is_authorized方法。问题是它需要一个B类的实例,由于它需要网络连接和其他东西,因此在隔离构造时非常复杂。我如何模拟这个类并提供只有get_name方法的东西。这样我就可以创建A类并测试方法。

2 个答案:

答案 0 :(得分:1)

通过使用mock库,您可以修补B类并将其替换为MagicMock()对象。 mock库的设计完全是为了完成这些工作,并打破有问题的对象或真实资源的硬依赖。

在您的简单示例中,完整的测试将是:

<强> module_a.py

class B():
    def __init__(self):
        print("The awful B class!!!")

    def get_name(self):
        print("The awful B.get_name() method!!!")


class A():
    def __init__(self):
        self.b = B()

    def is_authorized(self):
        name = self.b.get_name()
        if name == 'admin':
            return True
        else:
            return False

<强> module_a_test.py

import unittest
from unittest.mock import patch
from module_a import A


class MyTestCase(unittest.TestCase):

    # patch B class in a_module by a MagicMock instance
    # mock_b_constructor passed to test method
    @patch("module_a.B")
    def test_a(self, mock_b_constructor):
        # B() return value will be the B() instance assigned to a.b property
        mock_b = mock_b_constructor.return_value
        # Now start test:
        a = A()
        # Ok! b is our mock...
        self.assertIs(a.b, mock_b)
        # Not authorized
        self.assertFalse(a.is_authorized())
        mock_b.get_name.return_value = 'admin'
        # Yeah!!! we are admin
        self.assertTrue(a.is_authorized())
        # Sanity check
        mock_b.get_name.return_value = 'guest'
        self.assertFalse(a.is_authorized())

Patch仅适用于您的测试方法上下文。这是一个简单直接的例子,说明如何使用来自mock的模拟和补丁,但真实案例可能会更复杂一些。

答案 1 :(得分:0)

为B创建一个模拟类,例如:

class MockB(object):
    def get_name(self):
        return 'admin'

然后,在您的测试用例中,将A包装在一个使用MockB而不是常规B的类中:

class TestA(A):
    def __init__(self):
        self.b = MockB()

虽然,说实话,我不确定这证明了什么。