Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/351.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/69.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Python中模拟不可变类的成员_Python_Unit Testing_Sqlite_Mocking - Fatal编程技术网

在Python中模拟不可变类的成员

在Python中模拟不可变类的成员,python,unit-testing,sqlite,mocking,Python,Unit Testing,Sqlite,Mocking,我有一个使用sqlite3数据库的类,我想为它编写一个测试套件。特别是,我想检查是否使用正确的SQL命令调用了sqlite3.Cursor.execute。但是,我在模拟这个方法时遇到了麻烦,因为sqlite3.Cursor似乎是用C编写的,因此类是不可变的。这意味着我不能只修补execute方法,但是如果我尝试修补整个类,断言将失败,表示从未调用execute 下面是我迄今为止最好的尝试,但是断言失败了,说没有调用。对于我做错了什么,我希望你能给我一些建议。谢谢 myclass.py test

我有一个使用sqlite3数据库的类,我想为它编写一个测试套件。特别是,我想检查是否使用正确的SQL命令调用了
sqlite3.Cursor.execute
。但是,我在模拟这个方法时遇到了麻烦,因为
sqlite3.Cursor
似乎是用C编写的,因此类是不可变的。这意味着我不能只修补execute方法,但是如果我尝试修补整个类,断言将失败,表示从未调用execute

下面是我迄今为止最好的尝试,但是断言失败了,说没有调用。对于我做错了什么,我希望你能给我一些建议。谢谢

myclass.py test_myclass.py
sqlite3
是C扩展,不能修补C调用。无论如何,您都不应该测试
sqlite3
行为,而应该只测试如何编写调用
sqlite3
模块的代码

您可以做的是修补
sqlite3.connect()
方法,并检查您的代码是否以正确的方式调用API:

class MyClassTestCase(unittest.TestCase):
    @patch('sqlite3.connect', autospec=True)
    def test_query(self, mock_connect):
        mock_cursor = mock_connect.return_value.cursor.return_value
        mc = myclass.MyClass()
        mc.query('test')
        mock_cursor.execute.assert_called_with('test')
注:

  • 我修补了
    sqlite3.connect
    绝对路径,而不是
    myclass….
    做同样的事情,但更清楚(
    sqlite3.connect
    myclass.sqlite3.connect
    是完全相同的参考)
  • 我正在使用
    autospec=True
    来避免类似中解释的奇怪错误
  • 考虑编写自己的包装器并在测试中模拟它:您的代码将更易于测试,与
    sqlite3
    模块的耦合更少

  • 还有另一种方法

    您可以修补
    myclass.sqlite3
    并模拟,然后模拟
    connect().cursor().execute的返回值

    import unittest
    from mock import patch
    from myclass import MyClass
    
    
    class MyClassTestCase(unittest.TestCase):
        def test_query(self, mock_sql_cursor):
            with patch('uploads.myclass.sqlite3') as mocksql:
                mc = MyClass()
                mc.query('test')
                mock_sql_cursor.connect().cursor().execute.assert_called_with('test')
    

    答案灵感来源于

    看一看感谢这很有效。只有一个问题——为什么
    sqlite3
    membership.sqlite3
    是同一个名称空间?我也考虑编写一个包装类,但是添加不必要的抽象只是为了使我的代码适合测试套件似乎是愚蠢的。Python中没有命名空间。通过
    import sqlite3
    sqlite3
    模块引用复制到模块中,并将其称为
    sqlite3
    。这意味着通过
    myclass.sqlite3.connect
    您指向的引用与
    sqlite3.connect
    指向的引用相同,处处似乎都强调了在使用某个东西的地方而不是在调用补丁的地方进行补丁的重要性。如果对一个模块的所有引用都相同,为什么有必要这样做?因为您应该在使用它的地方修补引用,而不是浏览它。修补您使用的引用是在本地进行更改的好规则,但是您应该确切地知道您正在做什么。我喜欢通过使用绝对路径来明确它,在那里它是相同的。谢谢。这很有效,似乎是最简洁的解决方案。请注意在测试代码中使用mock的调用,而不是
    return\u value
    。Mock
    ()
    调用保留给生产代码,并且
    返回值在测试中非常有用,以便在不更改Mock状态的情况下具有相同的值。
    
    class MyClassTestCase(unittest.TestCase):
        @patch('sqlite3.connect', autospec=True)
        def test_query(self, mock_connect):
            mock_cursor = mock_connect.return_value.cursor.return_value
            mc = myclass.MyClass()
            mc.query('test')
            mock_cursor.execute.assert_called_with('test')
    
    import unittest
    from mock import patch
    from myclass import MyClass
    
    
    class MyClassTestCase(unittest.TestCase):
        def test_query(self, mock_sql_cursor):
            with patch('uploads.myclass.sqlite3') as mocksql:
                mc = MyClass()
                mc.query('test')
                mock_sql_cursor.connect().cursor().execute.assert_called_with('test')