Mocking a python database query - python

I am trying to test by mocking a database query, but receiving an error:
Asssertion error:AssertionError: Expected call: execute()
Not called
and create_table() not defined.
I want to execute() to be called and use create_table() to return response for asserting against pre-defined values.
app.py
from flask import Flask,g
#app.before_request
def before_request():
g.db = mysql.connector.connect(user='root', password='root', database='mysql')
def create_table():
try:
cur = g.db.cursor() #here g is imported form Flask module
cursor.execute ('CREATE TABLE IF NOT EXISTS Man (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(40)')
data = dict(Table='Man is created')
resp = jsonify(data)
cursor.close()
return resp
test.py
import unittest
from app import *
from mock import patch
class Test(unittest.TestCase):
def test_create(self):
with patch("app.g") as mock_g:
mock_g.db.cursor()
mock_g.execute.assert_called_with()
resp = create_table()
assertEqual(json, '{"Table":"Testmysql is created","Columns": ["id","name","email"]}')
What am I doing wrong?Can someone please tell me how to fix it

I believe you need to add your changes before closing the cursor, or the execute won't occur. Try adding cursor.commit() before (or instead of) cursor.close().

Related

Why open the same one database with sqlalchemy, but get different, how can I update it?

I write some tests with pytest, I want to test create user and email with post method.
With some debug, I know the issue is I open two databases in memory, but they are same database SessionLocal().
So how can I fix this, I try db.flush(), but it doesn't work.
this is the post method code
#router.post("/", response_model=schemas.User)
def create_user(
*,
db: Session = Depends(deps.get_db), #the get_db is SessionLocal()
user_in: schemas.UserCreate,
current_user: models.User = Depends(deps.get_current_active_superuser),
) -> Any:
"""
Create new user.
"""
user = crud.user.get_by_email(db, email=user_in.email)
if user:
raise HTTPException(
status_code=400,
detail="The user with this username already exists in the system.",
)
user = crud.user.create(db, obj_in=user_in)
print("====post====")
print(db.query(models.User).count())
print(db)
if settings.EMAILS_ENABLED and user_in.email:
send_new_account_email(
email_to=user_in.email, username=user_in.email, password=user_in.password
)
return user
and the test code is:
def test_create_user_new_email(
client: TestClient, superuser_token_headers: dict, db: Session # db is SessionLocal()
) -> None:
username = random_email()
password = random_lower_string()
data = {"email": username, "password": password}
r = client.post(
f"{settings.API_V1_STR}/users/", headers=superuser_token_headers, json=data,
)
assert 200 <= r.status_code < 300
created_user = r.json()
print("====test====")
print(db.query(User).count())
print(db)
user = crud.user.get_by_email(db, email=username)
assert user
assert user.email == created_user["email"]
and the test result is
> assert user
E assert None
====post====
320
<sqlalchemy.orm.session.Session object at 0x7f0a9f660910>
====test====
319
<sqlalchemy.orm.session.Session object at 0x7f0aa09c4d60>
Your code does not provide enough information to help you, the key issues are probably in what is hidden and explained by your comments.
And it seems like you are confusing sqlalchemy session and databases. If you are not familiar with these concepts, I highly recommend you to have a look at SQLAlchemy documentation.
But, looking at your code structure, it seems like you are using FastAPI.
Then, if you want to test SQLAlchemy with pytest, I recommend you to use pytest fixture with SQL transactions.
Here is my suggestion on how to implement such a test. I'll suppose that you want to run the test on your actual database and not create a new database especially for the tests. This implementation is heavily based on this github gist (the author made a "feel free to use statement", so I suppose he is ok with me copying his code here):
# test.py
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
from fastapi.testclient import TestClient
from myapp.models import BaseModel
from myapp.main import app # import your fastapi app
from myapp.database import get_db # import the dependency
client = TestClient(app)
# scope="session" mean that the engine will last for the whole test session
#pytest.fixture(scope="session")
def engine():
return create_engine("postgresql://localhost/test_database")
# at the end of the test session drops the created metadata using fixture with yield
#pytest.fixture(scope="session")
def tables(engine):
BaseModel.metadata.create_all(engine)
yield
BaseModel.metadata.drop_all(engine)
# here scope="function" (by default) so each time a test finished, the database is cleaned
#pytest.fixture
def dbsession(engine, tables):
"""Returns an sqlalchemy session, and after the test tears down everything properly."""
connection = engine.connect()
# begin the nested transaction
transaction = connection.begin()
# use the connection with the already started transaction
session = Session(bind=connection)
yield session
session.close()
# roll back the broader transaction
transaction.rollback()
# put back the connection to the connection pool
connection.close()
## end of the gist.github code
#pytest.fixture
def db_fastapi(dbsession):
def override_get_db():
db = dbsession
try:
yield db
finally:
db.close()
client.app.dependency_overrides[get_db] = override_get_db
yield db
# Now you can run your test
def test_create_user_new_email(db_fastapi):
username = random_email()
# ...

oracle query in python - The return type must be a string, dict, tuple

I'm trying to show the output of a query on the web api endpoint but it isn't working.
The code below is returning TypeError: The view function did not return a valid response. The return type must be a string, dict, tuple, Response instance, or WSGI callable, but it was a int.
I already tried different ways but no luck. What am I missing here?
from flask import Flask, jsonify, request, render_template
from flask_cors import CORS
import cx_Oracle
import os
import json
import logging
app = Flask(__name__)
app.logger.disabled = True
log = logging.getLogger('werkzeug')
log.disabled = True
#app.route('/query')
def query1():
connection = cx_Oracle.connect(user='superuser', password='mypass1', dsn='moon.my-org.local:1521/db1_sql')
cursor = connection.cursor()
cursor.execute("""SELECT * from users""")
result = cursor.fetchone()
if result == None:
return("No results")
exit
else:
while result:
return result[0]
result = cursor.fetchone()
cursor.close()
connection.close()
port = int(os.getenv("PORT"))
app.run(host='0.0.0.0', port=port, debug=True)
If the problem is just that you're returning an integer instead of a string, then it's a simple matter to transform it into a string with str():
return str(result[0])
However, you have another problem -- that return statement is inside a loop, which means only the very first result will be returned and then the function will exit altogether. The loop won't execute past the first iteration.

Python OOP programming issue when using MySQL connect

I am trying to write a backend that will be used for an IOS app. I know it will be technically the wrong way to do it but it wont be deployed.
My issues is i get the error
self.cursor(query)
TypeError: 'CMySQLCursor' object is not callable
This happens when i run the following from main.py
import database
db = database.database()
staff = db.getData("SELECT * FROM timesheets.staff")
Finally this is my database.py code
import mysql.connector
class database :
conn = ""
cursor = ""
def __init__(self):
self.conn = mysql.connector.connect(user='james',
password='timeismoney',
host='hallfamily.mycrestron.com',
database='timesheets',
port='6033')
self.cursor = self.conn.cursor()
print("Done")
def getData(self, query):
#Checking if the user has applied a string
if isinstance(query, str):
self.cursor(query)
else:
return "You have provided a request that cant be processed"
#Fetching all the results
result = self.cursor.fetchall()
#Returning back to the user
return result
def postData(self):
print("Coming soon")
def close(self):
self.conn.close()
Instead of:
self.cursor(query)
Try this:
self.cursor.execute(query)

sqlite3 database is locked(using bottle)

I'm trying to use Bottle framework in python with sqlite3. Then I made a Todo List application but when I tried to post a data at the first time the error happened differently from above. The second time 'database is locked' happened.
Can anyone help?
#_*_ coding:utf-8- _*_
import os, sqlite3
from bottle import route, run, get, post, request, template
#sqlite from here----------------
dbname = "todo.db"
connection = sqlite3.connect(dbname)
dbcontrol = connection.cursor()
#Making table from here--------------------
create_table = '''create table todo_list (todo text)'''
#route("/")
def index():
todo_list = get_todo()
return template("index", todo_list=todo_list)
I think I need more specific code here.
#route("/enter", method=["POST"])
def enter():
conn = sqlite3.connect("todo.db")
todo=request.POST.getunicode("todo_list")
save_todo(todo)
return redirect("/")
def save_todo(todo):
connection = sqlite3.connect('todo.db')
dbcontrol = connection.cursor()
insert="insert into todo_list(todo) values('{0}')".format(todo)
dbcontrol.execute(insert)
connection.commit()
def get_todo():
connection=sqlite3.connect('todo.db')
dbcontrol = connection.cursor()
select = "select * from todo_list"
dbcontrol.execute(select)
row = dbcontrol.fetchall()
return row
run(host="localhost", port=8080, debug=True)
Install the bottle-sqlite with:
$ pip install bottle-sqlite
An example from the plugin
import bottle
app = bottle.Bottle()
plugin = bottle.ext.sqlite.Plugin(dbfile='/tmp/test.db')
app.install(plugin)
#app.route('/show/:item')
def show(item, db):
row = db.execute('SELECT * from items where name=?', item).fetchone()
if row:
return template('showitem', page=row)
return HTTPError(404, "Page not found")
Important notes from the plugin
Routes that do not expect a db keyword argument are not affected.
The connection handle is configured so that sqlite3.Row objects can be
accessed both by index (like tuples) and case-insensitively by name.
At the end of the request cycle, outstanding transactions are
committed and the connection is closed automatically. If an error
occurs, any changes to the database since the last commit are rolled
back to keep the database in a consistent state.
Also take a look at Configuration section.

Monkey patch cursor.execute() and close() or not?

"Trying to unit test my code using unittest.mock python library".
I have code which is running database queries very similar to this:
app.py:
from flask import g
import mysql.connector
#app.route('/')
def create_table():
g.db=mysql.connector.connect("credentials")
cursor = g.db.cursor()
cursor.execute('CREATE TABLE IF NOT EXISTS test(id INT NOT NULL PRIMARY KEY, name VARCHAR(40),email VARCHAR(40) NOT NULL)')
cursor.close()
g.db.close()
I have mocked my g.db using the below method, but from here I am struggling with how to mock cursor.execute() and cursor.close(). Any help would be appreciated.
def testtable():
with patch('app.mysql.connector') as mock_mysql_connector:
create_table()
print g.db
#mock execute and close#
On printing g.db I'm getting Mock name and id, which I believe that means g.db is mocked, but I have no clue how I should mock execute() and close().
Do I have to do monkey patching?
If yes, please provide a hint how to monkey patch them?
If no, then what is another way to mock them?
You could use patch.multiple:
from mock import patch, DEFAULT
with patch.multiple('app', mysql=DEFAULT, g=DEFAULT) as dict:
# Mocking
connector = dict[̈́'mysql'].connector
db = connector.connect.return_value
cursor = db.cursor.return_value
# Run function to test
create_table()
# Assertions
assert dict['g'].db == db
db.close.assert_called_once_with()
cursor.close.assert_called_once_with()
connector.connect.assert_called_once_with("credentials")

Categories

Resources