Mocking a module selectively in Python 3.7 - python

I am trying to write unit test for one of my module using pymysql as follows:
def execute(self, query):
try:
conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='',
db='mysql')
cur = conn.cursor()
cur.execute(query)
except pymysql.Error as e:
raise
except Exception as e:
raise
While writing unit test for the above function, I am mocking pysql as follows #patch(my_package.my_module.pymysql). As a result pymysql.erroris also becoming mocked. So while testing the failure scenario as follows:
#patch('my_package.my_module.pymysql')
def test_execute_with_failure(self, mock_pymysql):
...
self.my_obj.cur.execute.side_effect = pymysql.Error
with self.assertRaises(pymysql.Error) as context:
_ = self.my_obj.execute(query="SELECT FOO FROM BAR")
I am getting the below error:
TypeError: catching classes that do not inherit from BaseException is not allowed
In this regard I have gone through this: Can't catch mocked exception because it doesn't inherit BaseException. But I am not getting how to make this work for pymysql.
EDIT 1: As suggested, I have tried to mock only pymysql.connect as follows:
#patch('my_package.my_module.pymysql.connect')
def test_execute_with_failure(self, mock_pymysql_conn):
cursor = MagicMock()
mock_pymysql_conn.return_value.cursor = cursor
self.my_obj.cur = cursor
self.my_obj.cur.execute.side_effect = pymysql.Error
with self.assertRaises(pymysql.Error) as context:
_ = self.my_obj.execute(query="SELECT FOO FROM BAR")
But, I am getting here, as follows:
E AttributeError: <module 'pymysql' from '/my_project/venv/lib/python3.7/site-packages/pymysql/__init__.py'> does not have the attribute ''
EDIT 2: I have tried with below approach as well:
#patch('my_package.my_module.pymysql')
def test_execute_with_failure(self, mock_pymysql):
conn = Mock()
mock_pymysql.connect.return_value = conn
cursor = MagicMock()
conn.return_value.cursor = cursor
self.my_obj.cur = cursor
mock_pymysql.Error = type('PymysqlError', (Exception,), {})
self.my_obj.cur.execute.side_effect = mock_pymysql.Error
with self.assertRaises(mock_pymysql.Error) as context:
_ = self.my_obj.execute(query="SELECT * FROM TEST")
Still the same error as Edit 1.

Related

How can i use sqlalchemy stream with postgres if it is crashes with NoActiveSQLTransactionError?

from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
async with create_async_engine(my_db_url, isolation_level="AUTOCOMMIT").connect() as conn:
await conn.begin()
assert conn.in_transaction() is True
result = await conn.stream(text(f"select * from existent_table"))
async for row in result:
print(f"{row}")
This is a literally example from the manual https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html#sqlalchemy.ext.asyncio.AsyncConnection.stream
But i am getting this error:
self = <AdaptedConnection <asyncpg.connection.Connection object at 0x7fa0c86c6260>>
error = NoActiveSQLTransactionError('cursor cannot be created outside of a transaction')
def _handle_exception(self, error):
if self._connection.is_closed():
self._transaction = None
self._started = False
if not isinstance(error, AsyncAdapt_asyncpg_dbapi.Error):
exception_mapping = self.dbapi._asyncpg_error_translate
for super_ in type(error).__mro__:
if super_ in exception_mapping:
translated_error = exception_mapping[super_](
"%s: %s" % (type(error), error)
)
translated_error.pgcode = (
translated_error.sqlstate
) = getattr(error, "sqlstate", None)
> raise translated_error from error
E sqlalchemy.exc.DBAPIError: (sqlalchemy.dialects.postgresql.asyncpg.Error) <class 'asyncpg.exceptions.NoActiveSQLTransactionError'>: cursor cannot be created outside of a transaction
E [SQL: select * from table_fca3cc5354ab445cb11cafd79791a9c5]
E (Background on this error at: https://sqlalche.me/e/14/dbapi)
How is it possible if i already asserded transaction exists? And how to get it work?
If i remove isolation level - error disappears, but stream results is empty. Not sure i should really comment off isolation level.

How to access the instance variable?

I have used a 'class' named 'CMS'. Also,i have defined a method def ConnectDB(),Where i have loaded the driver and created a cursor object to execute a query.Whenever i try to access the cursor object,it's giving me an attribute error.
obj.cursor.execute(sql,v)
AttributeError: CMS instance has no attribute 'cursor'
How do I resolve it?Refer the code below to understand the problem statement clearly.Thanks in advance.
class CMS:
def ConnectDB(self):
server = '192.121.063.012'
database = 'fghr_db'
username = 'abcdef'
password = 'zzzzzzz'
connStr = 'DRIVER=/opt/microsoft/msodbcsql17/lib64/libmsodbcsql- 17.2.so.0.1;SERVER='+server+';DATABASE='+database+';'+ 'UID='+username+'; PWD='+ password+';Port=1270'
cnxn = pyodbc.connect(connStr)
cursor = cnxn.cursor()
def ReadCsv(self,filepath):
data = csv.reader(open(filepath))
print data
h=data.next()
result=[]
for x in data:
c=list(x)
result.append(c)
return result
obj=CMS()
obj.ConnectDB()
sql="INSERT INTO StudentCsv VALUES (?,?,?,?,?)"
z=obj.ReadCsv("Student.csv")
for v in z:
print v
obj.cursor.execute(sql,v)
obj.cnxn.commit()
ERROR getting like below:
obj.cursor.execute(sql,v) AttributeError: CMS instance has no
attribute 'cursor'

Python class database constructor

I am trying to convert my script into a class, Since I will be using the connect method everytime,
I want to place it in the constructor, and then call it in the other functions.
I am new classes in Python, This is a
from datetime import date
import pymssql
import sys
class PHQuery:
def __init__(self):
self.conn = self.conn()
def get_stock_by_sku(self, sku):
if self.conn["code"] < 0:
return {"code":-1,"error":conn["error"]}
cursor = self.conn.cursor(as_dict=True)
try:
cursor.execute('''
SELECT NUMBER, UNITS, LOW, UNCOST, PRICE1, ONORDER
FROM STOCK
WHERE NUMBER=%s''', sku)
return {"code":1,"data":cursor.fetchone()}
except Exception as e:
return {"code":-2,"error":e}
def conn(self):
try:
conn = pymssql.connect(server='server', user='user', password='pwd', database='db', timeout=0, login_timeout=60, charset='UTF-8', as_dict=False, port='1433')
return {"code":1,"data":conn}
except Exception as e:
return {"code":-1,"error":e}
OUTPUT ERRORS:
File "test.py", line 3, in
print testObject.get_stock_by_sku('BK31')
TypeError: unbound method get_stock_by_sku() must be called with PHQuery instance as first argument (got str instance instead)
THIS IS THE CALL TO THE METHOD
from query import PHQuery
testObject = PHQuery
print testObject.get_stock_by_sku('BK31')
Here is my goal
data = {"stock_id" : "12345"}
qobject = PHQuery()
qobject.get_stock_by_sku(data["stock_id"])
and return the same data my script returns:
The script below is working perfectly fine, I just need to make it a class.
THANK YOU IN ADVANCE.
WORKING CODE:
import time
from datetime import date
import pymssql
import sys
def conn():
try:
conn = pymssql.connect(server='', user='', password='', database='', timeout=0, login_timeout=60, charset='UTF-8', as_dict=False, port='1433')
return {"code":1,"data":conn}
except Exception as e:
return {"code":-1,"error":e}
conn = conn()
def get_stock_by_sku(conn,sku):
""" Returns stock information when passing a sku"""
if conn["code"] < 0:
return {"code":-1,"error":conn["error"]}
cursor = conn["data"].cursor(as_dict=True)
try:
cursor.execute('''
SELECT NUMBER, UNITS, LOW, UNCOST, PRICE1, ONORDER
FROM STOCK
WHERE NUMBER=%s''', sku)
return {"code":1,"data":cursor.fetchone()}
except Exception as e:
return {"code":-2,"error":e}

Mocking Only One Function of a Class

I would like to write a unit test that can ensure a SQL statement in a function call is schematically correct. It should test the execution of this call. I would then like to mock the call to commit, so that no insertions to the database take place. I'm using psycopg2 for my tests.
I have a function like:
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()
when calling test_insert(1,2,3) I see the row inserted into the table. Now I try to mock the call. I've taken a few approaches so far:
#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)
This seems to work but does not actually call the execution statement. test_insert(1,4,'xyz') fails for instance while test(1,4,'xyz') does not. Next I tried to mock just the commit method of the connection class in 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)
but this gives me a syntax error
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'
Is there a good way to do what I am trying to do?
I assume you are using pytest and it is not a good practice to name your functions starting with test_ if they are not actual tests as this will probably raise problems with the testing framework. Therefore I slightly modified your initial snippet as follows and I named the module 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()
Next, I created the test for your method by taking into account how patch works and where to patch. As you are dealing with os environment variables this question can help you understand why I mocked it that way.
An example implementation of the test could be as follows:
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

how to make a python-mysqldb template?

I've learn some basics about python-mysqldb ,when I want to define anther function for query,I have to write (connect ,cursor...try ..) repeatedly
so I want to design a template like jdbcTemplate (Java EE, Spring)
my code is:
def DBV():
def templateFN(fn):
logging.basicConfig(level=logging.INFO)
log = logging.getLogger('DB')
conn = MySQLdb.connect(user='root',passwd='247326',db='lucky',charset="utf8",cursorclass=MySQLdb.cursors.DictCursor);
cursor = conn.cursor()
def wrap(data=None):
try:
return fn(cursor=cursor,data=data)
#conn.commit()
except Exception ,e:
conn.rollback()
log.error('%s, transaction rollback',e)
finally:
cursor.close()
conn.close()
return wrap
class DB():
#templateFN
def insertTest(self,cursor,data=None):
data = {
'field':'this is a test',
'name':'this is a name'
}
return cursor.execute('insert into test(field,name) values(%(field)s,%(name)s)',data)
return DB()
db = DBV()
print 'return value',db.insertTest(data="ok")
Traceback (most recent call last):
File "D:\WorkSpaces\Aptana Studio 3 Workspace\VLuck\src\com\test.py", line 164, in
print 'return value',db.insertTest(data="ok")
TypeError: wrap() got multiple values for keyword argument 'data'
but failed,how should I do it right
Here's a solution I came up with, inspired by another answer:
def connect_mysql(func):
# Assign value of 'self' to be default func
func.func_defaults = func.func_defaults[:-1] + (func,)
func._cnx = mysql.connector.connect(**CONFIG)
func._cursor = func._cnx.cursor()
return func
#connect_mysql
def test(data, self=None):
self._cursor.execute("SELECT %(c1)s", data)
print(self._cursor.fetchall())
test({'c1': 1})

Categories

Resources