只模拟一个班级的一个功能

时间:2017-04-05 23:00:08

标签: python unit-testing mocking psycopg2

我想编写一个单元测试,可以确保函数调用中的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中模拟连接类的commit方法:

@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'

有没有办法做我想做的事情?

1 个答案:

答案 0 :(得分:0)

我假设您正在使用pytest,如果它们不是实际测试,则以test_开头命名函数不是一个好习惯,因为这可能会引发测试框架的问题。因此,我稍微修改了您的初始代码段,并将模块命名为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