Psycopg2Plugin Guide¶
Psycopg2Plugin intercepts psycopg2.connect() and returns a fake connection object that routes all operations through a session script. It requires the psycopg2 extra.
Installation¶
Setup¶
In pytest, access Psycopg2Plugin through the bigfoot.psycopg2_mock proxy. It auto-creates the plugin for the current test on first use:
import bigfoot
def test_select_users():
(bigfoot.psycopg2_mock
.new_session()
.expect("connect", returns=None)
.expect("execute", returns=[[1, "Alice"], [2, "Bob"]])
.expect("close", returns=None))
with bigfoot:
import psycopg2
conn = psycopg2.connect(dsn="dbname=myapp")
cur = conn.cursor()
cur.execute("SELECT id, name FROM users")
rows = cur.fetchall()
conn.close()
assert rows == [[1, "Alice"], [2, "Bob"]]
bigfoot.psycopg2_mock.assert_connect(dsn="dbname=myapp")
bigfoot.psycopg2_mock.assert_execute(sql="SELECT id, name FROM users", parameters=None)
bigfoot.psycopg2_mock.assert_close()
For manual use outside pytest, construct Psycopg2Plugin explicitly:
from bigfoot import StrictVerifier
from bigfoot.plugins.psycopg2_plugin import Psycopg2Plugin
verifier = StrictVerifier()
pg = Psycopg2Plugin(verifier)
Each verifier may have at most one Psycopg2Plugin. A second Psycopg2Plugin(verifier) raises ValueError.
State machine¶
disconnected --connect--> connected --execute--> in_transaction
in_transaction --execute--> in_transaction
in_transaction --commit--> connected
in_transaction --rollback--> connected
connected --close--> closed
in_transaction --close--> closed
psycopg2.connect() transitions from disconnected to connected. Each execute() moves the connection into in_transaction. commit() and rollback() return it to connected. close() can be called from either connected or in_transaction.
Scripting a session¶
Use new_session() to create a SessionHandle and chain .expect() calls:
(bigfoot.psycopg2_mock
.new_session()
.expect("connect", returns=None)
.expect("execute", returns=[["row1"], ["row2"]])
.expect("commit", returns=None)
.expect("close", returns=None))
expect() parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
method |
str |
required | Step name: "connect", "execute", "commit", "rollback", or "close" |
returns |
Any |
required | Value returned by the step (see below) |
raises |
BaseException \| None |
None |
Exception to raise instead of returning |
required |
bool |
True |
Whether an unused step causes UnusedMocksError at teardown |
Return values by step¶
| Step | returns type |
Description |
|---|---|---|
connect |
None |
The fake connection is constructed automatically |
execute |
list[list] |
Rows returned by fetchone(), fetchall(), or fetchmany() |
commit |
None |
No return value |
rollback |
None |
No return value |
close |
None |
No return value |
Connection parameters¶
The plugin supports both DSN and keyword-based connection parameters:
# DSN style
psycopg2.connect(dsn="dbname=myapp host=localhost")
# Keyword style
psycopg2.connect(host="localhost", port=5432, dbname="myapp", user="admin")
The assert_connect() helper accepts whichever parameters were used:
# For DSN connections
bigfoot.psycopg2_mock.assert_connect(dsn="dbname=myapp host=localhost")
# For keyword connections
bigfoot.psycopg2_mock.assert_connect(host="localhost", port=5432, dbname="myapp", user="admin")
Cursor behavior¶
The fake connection's cursor() returns a cursor proxy. Call execute() on the cursor, then use standard fetch methods:
(bigfoot.psycopg2_mock
.new_session()
.expect("connect", returns=None)
.expect("execute", returns=[[1, "Alice"], [2, "Bob"], [3, "Carol"]])
.expect("close", returns=None))
with bigfoot:
conn = psycopg2.connect(dsn="dbname=test")
cur = conn.cursor()
cur.execute("SELECT id, name FROM users")
# fetchone() returns rows one at a time
first = cur.fetchone() # [1, "Alice"]
# fetchmany(size) returns a batch
batch = cur.fetchmany(1) # [[2, "Bob"]]
# fetchall() returns remaining rows
rest = cur.fetchall() # [[3, "Carol"]]
conn.close()
Asserting interactions¶
Each step records an interaction on the timeline. Use the typed assertion helpers on bigfoot.psycopg2_mock:
assert_connect(**kwargs)¶
Asserts the next connect interaction. Pass whichever connection fields were used.
assert_execute(*, sql, parameters)¶
Asserts the next execute interaction. Both sql and parameters are required.
bigfoot.psycopg2_mock.assert_execute(
sql="INSERT INTO users (name) VALUES (%s)",
parameters=("Alice",),
)
assert_commit()¶
Asserts the next commit interaction. No fields are required.
assert_rollback()¶
Asserts the next rollback interaction. No fields are required.
assert_close()¶
Asserts the next close interaction. No fields are required.
Full example¶
import psycopg2
import bigfoot
def save_user(name, email):
conn = psycopg2.connect(host="localhost", dbname="app", user="app")
cur = conn.cursor()
cur.execute("INSERT INTO users (name, email) VALUES (%s, %s)", (name, email))
conn.commit()
conn.close()
def test_save_user():
(bigfoot.psycopg2_mock
.new_session()
.expect("connect", returns=None)
.expect("execute", returns=[])
.expect("commit", returns=None)
.expect("close", returns=None))
with bigfoot:
save_user("Alice", "alice@example.com")
bigfoot.psycopg2_mock.assert_connect(host="localhost", dbname="app", user="app")
bigfoot.psycopg2_mock.assert_execute(
sql="INSERT INTO users (name, email) VALUES (%s, %s)",
parameters=("Alice", "alice@example.com"),
)
bigfoot.psycopg2_mock.assert_commit()
bigfoot.psycopg2_mock.assert_close()