【问题标题】:Mocking Only One Function of a Class只模拟一个类的一个函数
【发布时间】:2017-04-05 23:00:08
【问题描述】:

我想编写一个单元测试,可以确保函数调用中的 SQL 语句在示意图上是正确的。它应该测试这个调用的执行。然后我想模拟对提交的调用,这样就不会发生对数据库的插入。我正在使用 psycopg2 进行测试。

我有一个类似的功能:

def test_insert(a, b, c):
    con = psycopg2.connect(os.environ['PGDB'])
    cur = con.cursor()
    cur.execute('insert into test_table values ({a}, {b}, {c})'.format(a=a, b=b, c=c))
    con.commit()
    con.close()

当调用test_insert(1,2,3) 时,我看到插入到表中的行。现在我尝试模拟通话。到目前为止,我已经采取了一些方法:

@mock.patch('psycopg2.connect')
def test(mock_connect, a, b, c):
    mock_con = mock_connect.return_value
    mock_con.commit.return_value = None
    insert_row(a, b, c)

这似乎有效,但实际上并没有调用执行语句。例如,test_insert(1,4,'xyz') 会失败,而 test(1,4,'xyz') 不会。接下来我尝试只模拟 psycopg2 中连接类的提交方法:

@mock.patch('psycopg2.extensions.connection.commit')
def test_insert(mock_commit, a, b, c):
    mock_commit.return_value = None
    insert_row(a,b,c)

但这给了我一个语法错误

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/a/.virtualenv/test/lib/python2.7/site-packages/mock/mock.py", line 1318, in patched
    patching.__exit__(*exc_info)
  File "/home/a/.virtualenv/test/lib/python2.7/site-packages/mock/mock.py", line 1480, in __exit__
    setattr(self.target, self.attribute, self.temp_original)
TypeError: can't set attributes of built-in/extension type 'psycopg2.extensions.connection'

有什么好方法可以做我想做的事吗?

【问题讨论】:

    标签: python unit-testing mocking psycopg2


    【解决方案1】:

    我假设您使用的是pytest,如果您的函数不是实际测试,那么以test_ 开头的函数命名不是一个好习惯,因为这可能会导致测试框架出现问题。因此,我将您的初始 sn-p 稍微修改如下,并将模块命名为 psyco.py

    import psycopg2
    import os
    
    def insert(a, b, c):
        con = psycopg2.connect(os.environ['PGDB'])
        import ipdb; ipdb.set_trace()
        cur = con.cursor()
        cur.execute('insert into test_table values ({a}, {b}, {c})'.format(a=a, b=b, c=c))
        con.commit()
        con.close()
    

    接下来,我通过考虑how patch workswhere to patch 为您的方法创建了测试。当您处理 os 环境变量时,this question 可以帮助您理解我为什么这样嘲笑它。

    测试的示例实现如下:

    from psyco import insert
    from unittest.mock import patch, Mock, MagicMock
    import os
    
    @patch.dict(os.environ,{'PGDB':'db_url'})
    @patch('psycopg2.connect')
    def test_insert_function(psycopg2_mock):
    
        x = 1
        y = 4
        z = 'xyz'
        sql_query = 'insert into test_table values ({0}, {1}, {2})'.format(x,y,z)
        insert(x,y,z)
        assert psycopg2_mock.return_value.cursor.call_count == 1
        psycopg2_mock.return_value.cursor.return_value.execute.assert_called_with(sql_query)
        assert psycopg2_mock.return_value.commit.call_count == 1
        assert psycopg2_mock.return_value.close.call_count == 1
    

    【讨论】:

    • 提问者没有尝试修补环境变量或connect。他们正在尝试修补commit
    • @user2357112 我明白你的意思,我假设因为它是一个单元测试,所以你不想有外部依赖。这就是为什么我建议同时检查function call is schematically correct 和模拟提交的调用,因为psycopg2.connect 调用是一个模拟
    猜你喜欢
    • 1970-01-01
    • 2018-09-04
    • 2019-11-01
    • 1970-01-01
    • 2019-04-18
    • 1970-01-01
    • 2017-07-23
    • 2020-09-05
    • 1970-01-01
    相关资源
    最近更新 更多