如何模拟数据库方法

时间:2018-12-18 11:57:49

标签: python database unit-testing mocking

我对于python和模拟一般来说还是很新的。因此,我尝试通过阅读文档找到解决问题的方法: https://docs.python.org/3/library/unittest.mock.html#magic-methods,下一篇文章:http://alexmarandon.com/articles/python_mock_gotchas/ 还有很多stackoverflow问题。但是我找不到解决办法。

我尝试模拟两个函数,这两个函数是创建数据库连接并将数据放入pandas dataFrame中。它们用于后功能(覆盖django后功能):

def post(self, request, *args, **kwargs):
    db_connection = du_db.get_connection(dbtype='mssql', username=crd.MS_SQL_USER, password=crd.MS_SQL_PWD)
    df = du_db.fetch_dataframe(sql, connection=db_connection)

在测试环境中,get_connection应该不返回任何内容,fetch_dataframe应该返回之前定义的熊猫数据框。

我的测试课程如下:

class IndexViewTest(TestCase):

@mock.patch('du_db.db.get_connection')
@mock.patch('du_db.db.fetch_dataframe')
def setUp(self, mock_get_connection, mock_fetch_dataframe):
    self.c = Client()
    mock_get_connection = mock_get_connection()
    mock_fetch_dataframe = mock_fetch_dataframe()
    mock_get_connection.return_value = ""
    df = {'lot_of_data': ['xy', 'z'], 'more_data': [8, 9]}
    mock_fetch_dataframe.return_value = pd.DataFrame(df)
    assert mock_get_connection is data_utils.db.get_connection()
    assert mock_fetch_dataframe is data_utils.db.fetch_dataframe()
    assert mock_get_connection.called
    assert mock_get_connection.called

# Lot of test-functions similar to this:
def test_valid_data(self):
    resp = self.c.post('/', data={'id': 3338})
    self.assertEqual(resp.status_code, 200)
    self.assertContains(resp, 'Hello', status_code=200)

我收到以下错误消息:

enter image description here

通过模拟替换原始功能无法正常工作。

感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

class IndexViewTest(TestCase):

    @mock.patch('du_db.db.get_connection')
    @mock.patch('du_db.db.fetch_dataframe')
    def setUp(self, mock_fetch_dataframe, mock_get_connection):
        self.c = Client()
        mock_get_connection = mock_get_connection() # remove this
        mock_fetch_dataframe = mock_fetch_dataframe() # remove this

当您调用上面的模拟时,它们会返回另一个新的模拟。通过分配相同的名称,您将失去对补丁模拟的引用。您将无法配置或检查它们。

        mock_get_connection.return_value = "" # this is NOT the mock you think
                                              # unless removing lines above.
                                              # And so on...
        df = {'lot_of_data': ['xy', 'z'], 'more_data': [8, 9]}
        mock_fetch_dataframe.return_value = pd.DataFrame(df)

        # data__utils or du_db ??
        assert mock_get_connection is data_utils.db.get_connection()                                                                 
        assert mock_fetch_dataframe is data_utils.db.fetch_dataframe()
        assert mock_get_connection.called
        assert mock_get_connection.called

    # Lot of test-functions similar to this:
    def test_valid_data(self):
        resp = self.c.post('/', data={'id': 3338})
        self.assertEqual(resp.status_code, 200)
        self.assertContains(resp, 'Hello', status_code=200)

编辑: 据我了解,您已经在pdb中看到了模拟。为了使测试按您的意愿工作,您需要修补使用这两个功能的每个测试功能,例如使用修补程序装饰器。然后,您必须在修补的测试函数中设置模拟。通常,您也可以在测试中断言,但不能在设置中断言。我了解到您是为了方便而在此处制​​作的,因为您对正在运行的代码有一些疑问。

如果您正在考虑在setUp中设置模拟以供其他测试功能使用(您不能那样做,则必须将它们存储在self中,然后在测试功能中再次进行管理),也许您对Mock文档中的这个示例感兴趣:

  

管理补丁的另一种方法是使用补丁方法:   开始和停止。这些使您可以将修补程序移到您的设置中   和tearDown方法。

>>> class MyTest(TestCase):
    ...
    def setUp(self):
        ...
        self.patcher = patch(’mymodule.foo’)
        ...
        self.mock_foo = self.patcher.start()
    ...
    ...
    def test_foo(self):
        ...
        self.assertTrue(mymodule.foo is self.mock_foo)
        ...
        ...
    def tearDown(self):
        ...
        self.patcher.stop()
        ...
>>> MyTest(’test_foo’).run()