SocketPlugin Guide¶
SocketPlugin intercepts socket.socket at the class level, patching connect, send, sendall, recv, and close. It is included in core bigfoot -- no extra required.
Setup¶
In pytest, access SocketPlugin through the bigfoot.socket_mock proxy. It auto-creates the plugin for the current test on first use:
import bigfoot
def test_echo_client():
(bigfoot.socket_mock
.new_session()
.expect("connect", returns=None)
.expect("sendall", returns=None)
.expect("recv", returns=b"pong")
.expect("close", returns=None))
with bigfoot:
import socket
sock = socket.socket()
sock.connect(("127.0.0.1", 9999))
sock.sendall(b"ping")
data = sock.recv(1024)
sock.close()
assert data == b"pong"
bigfoot.socket_mock.assert_connect(host="127.0.0.1", port=9999)
bigfoot.socket_mock.assert_sendall(data=b"ping")
bigfoot.socket_mock.assert_recv(size=1024, data=b"pong")
bigfoot.socket_mock.assert_close()
For manual use outside pytest, construct SocketPlugin explicitly:
from bigfoot import StrictVerifier
from bigfoot.plugins.socket_plugin import SocketPlugin
verifier = StrictVerifier()
sock = SocketPlugin(verifier)
Each verifier may have at most one SocketPlugin. A second SocketPlugin(verifier) raises ValueError.
State machine¶
connect() transitions from disconnected to connected. send(), sendall(), and recv() are self-loops on connected. close() transitions to closed.
Scripting a session¶
Use new_session() to create a SessionHandle and chain .expect() calls:
(bigfoot.socket_mock
.new_session()
.expect("connect", returns=None)
.expect("send", returns=5)
.expect("recv", returns=b"reply")
.expect("close", returns=None))
expect() parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
method |
str |
required | Step name: "connect", "send", "sendall", "recv", 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 |
No return value |
send |
int |
Number of bytes sent |
sendall |
None |
No return value (sendall returns None in the real API) |
recv |
bytes |
Data received from the socket |
close |
None |
No return value |
Asserting interactions¶
Each step records an interaction on the timeline. Use the typed assertion helpers on bigfoot.socket_mock:
assert_connect(*, host, port)¶
assert_send(*, data)¶
assert_sendall(*, data)¶
assert_recv(*, size, data)¶
Both size and data are required. size is the buffer size passed to recv(), and data is the bytes actually returned.
assert_close()¶
No fields are required.
Multiple connections¶
Sessions are consumed in registration order. The first socket.connect() pops the first queued session:
def test_two_connections():
(bigfoot.socket_mock
.new_session()
.expect("connect", returns=None)
.expect("recv", returns=b"first")
.expect("close", returns=None))
(bigfoot.socket_mock
.new_session()
.expect("connect", returns=None)
.expect("recv", returns=b"second")
.expect("close", returns=None))
with bigfoot:
s1 = socket.socket()
s2 = socket.socket()
s1.connect(("127.0.0.1", 9001))
s2.connect(("127.0.0.1", 9002))
assert s1.recv(1024) == b"first"
assert s2.recv(1024) == b"second"
s1.close()
s2.close()
bigfoot.socket_mock.assert_connect(host="127.0.0.1", port=9001)
bigfoot.socket_mock.assert_connect(host="127.0.0.1", port=9002)
bigfoot.socket_mock.assert_recv(size=1024, data=b"first")
bigfoot.socket_mock.assert_recv(size=1024, data=b"second")
bigfoot.socket_mock.assert_close()
bigfoot.socket_mock.assert_close()
Full example¶
import socket
import bigfoot
def fetch_status(host, port):
sock = socket.socket()
sock.connect((host, port))
sock.sendall(b"STATUS\r\n")
response = sock.recv(4096)
sock.close()
return response.decode()
def test_fetch_status():
(bigfoot.socket_mock
.new_session()
.expect("connect", returns=None)
.expect("sendall", returns=None)
.expect("recv", returns=b"OK 200\r\n")
.expect("close", returns=None))
with bigfoot:
result = fetch_status("monitoring.internal", 5000)
assert result == "OK 200\r\n"
bigfoot.socket_mock.assert_connect(host="monitoring.internal", port=5000)
bigfoot.socket_mock.assert_sendall(data=b"STATUS\r\n")
bigfoot.socket_mock.assert_recv(size=4096, data=b"OK 200\r\n")
bigfoot.socket_mock.assert_close()
InvalidStateError¶
Calling a method from the wrong state raises InvalidStateError immediately. For example, calling recv() before connect():
bigfoot.InvalidStateError: 'recv' called in state 'disconnected'; valid from: frozenset({'connected'})
Check the state machine diagram to ensure your session script matches the expected call order.