Choosing Between Parameterized Connections and Wrapper Structs for Racket DB Mocking
0 reputation · 07 Dec 2025, 22:54 UTC
0 reputation · 07 Dec 2025, 22:54 UTC
The goal is to write database‑integration tests that never use production credentials while keeping the test suite reliable and maintainable. Two documented strategies exist: rebinding the keyword arguments of `racket/db`'s `make-connection` with `parameterize` to inject test values, or defining a lightweight struct that carries connection details and passing an instance to query functions so tests can supply dummy values.
Constraints include ensuring that the parameter overrides are honoured by the underlying driver (no accidental real network calls), supporting older `racket/db` releases that lack the parameter‑friendly keyword arguments, and balancing the explicit‑dependency benefits of the wrapper struct against the boilerplate and refactoring effort it introduces. Which approach guarantees that `make-connection` uses the overridden parameters without risking a real connection? How does each choice affect the amount of code change required and the support for static analysis? Are there version‑specific caveats that make one strategy safer for projects constrained to pre‑6.12 `racket/db` releases?
34775 reputation · 07 Dec 2025, 23:04 UTC
The wrapper‑struct approach guarantees that make-connection never reaches a real database, regardless of the racket/db version. The parameterize technique is safe only when the library version exposes its connection arguments as parameters (releases ≥ 6.12).
racket/db ≥ 6.12 the keyword arguments (#:user, #:password, #:hostname, …) are read through corresponding parameters, so rebinding them with parameterize forces the driver to use the supplied dummy values and no network call is made.parameterize cannot override them, and the underlying driver may still attempt a real connection.struct test-db (user password db-name)) passed to a test‑specific query function lets you build a connection or call a mocked make-connection yourself, eliminating any reliance on the library’s internal parameter handling.If you wrap the test body in (parameterize ([make-connection-user "test"]) …) you are relying on an implementation detail that changed in version 6.12. When that detail is absent, the override is ignored and the driver may fall back to defaults or environment variables, risking a real network attempt.
The wrapper struct makes the dependency explicit: each query function receives the struct (or a connection factory) and decides how to obtain a connection. This requires updating call sites but gives the type checker a clear contract and avoids hidden reliance on library internals.
(require racket/version) then (version).(parameterize ([make-connection-user "test-user"]
[make-connection-password "test-pass"]
[make-connection-hostname "localhost"])
;; your test that calls query functions
)
(struct test-db (user password db-name) #:transparent)
test-db instance and use its fields to call make-connection or a mocked version.
(define (run-query db sql)
(define conn (make-connection #:user (test-db-user db)
#:password (test-db-password db)
#:database (test-db-db-name db)))
;; … use conn …
)
make-connection or use a test database that is guaranteed to be isolated.parameterize: minimal change (just add the wrapper), but the overridden arguments are invisible to the type checker; a future change to make-connection's keyword list could silently break the override.For projects locked to racket/db releases before 6.12, the parameterize strategy cannot be trusted to prevent real connections; the wrapper‑struct method is the only safe choice. If you are unsure of the library version, obtain it as a diagnostic detail before deciding.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.