"Nested use of with-mocks not allowed" - why?
I'm currently writing tests for a file that has a bunch of functions modifying shared state and, based on that state, performing I/O, sometimes using callbacks/thunks created in other functions. I want to write my tests in a way that mocks all the I/O operations, but none of the state management; however, if I'm in a with-mocks block that binds the mock I want to check (one used in a callback), I can't also be in one for the function that triggers the callback after some I/O.
(simplified use case:
#lang racket
(module+ test
(require mock/rackunit))
(require mock)
(struct event (time proc))
(define events '())
(define/mock (schedule-reminder! time message)
#:mock displayln #:as print-with #:with-behavior void
(set! events (cons (event time (thunk (displayln message))) events)))
(define/mock (check-events!)
#:mock current-seconds #:as get-time-with #:with-behavior (const 0)
(define time (current-seconds))
(for ([e events])
(when
(> time (event-time e))
((event-proc e))))
(set! events (filter (λ (e) (< time (event-time e))) events)))
(module+ test
(with-mocks schedule-reminder!
(schedule-reminder! 60 "Hello!")
#;; disallowed
(with-mocks check-events!
(check-events!)
(check-mock-num-calls print-with 0)
(with-mock-behavior ([get-time-with (const 100)])
(check-events!)
(check-mock-calls print-with
(list (arguments "Hello!"))))))
(with-mocks check-events!
(check-events!)
#;; print-with isn't bound here
(check-mock-num-calls print-with 0)
(with-mock-behavior ([get-time-with (const 100)])
(check-events!)
#;; or here
(check-mock-calls print-with
(list (arguments "Hello!"))))))
)
It seems like just removing the check for nested with-mocks gets this specific example working. However, I assume the check is there for some reason, so I won't PR yet.
It's been several years so my memory is hazy, but I think the check was there because it was a low-effort way to prevent (with-mocks foo (with-mocks foo _)) from happening.
I don't personally use mocks anymore, actually. I prefer making fakes instead, by which I mean simplified IO-free and low-dependency implementations of stateful interfaces.