So on my journey with Racket I Needed a Mock

Some things are simple. Database connections is not one of them. They're brittle, stateful, side-effect ridden… Any number of crimes against simplicity 😃 So they're excellent candidates for mocking. The documentation for the racket mock package talks about it a little but I figured I'd just post this little example to help others. And of course me when I can't remember how this works any more.

So, here's a little function that sets up a connection. Note, it's got zero error handling or any of the good stuff:

(define (reporter url #:connector [postgresql-connect postgresql-connect])
; connect to the db
(define pgc
  (postgresql-connect #:user "a_user"
                      #:database "my_db"
                      #:password "a_password"))
; find all the tables
(list-tables pgc))

The simple part of this is the connection. The racket db package has connectors for various databases. All you need to do is pick one and get cracking. I wanted postgres so this was easy. Similarly list-tables does the various obvious task of handing you a list of tables. At some point I want a url too define the database but at this stage in development I'm just checking the side effect.

The only funky bit of syntax is that keyword argument. In order to be able to use a mock I have two options:

  1. Allow the mock to be passed in as an argument
  2. Find some way to make the mock library swap out the real implementation at runtime.

Of course, I went with 1. So, I have a keyword argument called #:connector. #:connector has a default value and a variable name it gives the argument. In this case both are postgresql-connect.

I want to test the connection gets called with the right arguments.

(module+ test
(require rackunit)
(require mock)
(require mock/rackunit)

(test-case "it calls the db connector with the right arguments"
  (define connector-mock (mock #:behavior postgresql-connect ))
  (reporter "not://a.url/test" #:connector connector-mock)
  (check-mock-called-with? connector-mock (arguments #:database "my_db"
                                                     #:password "a_password"
                                                     #:user "a_user"))))

So my connector-mock mock is setup to mimic postgresql-connect. Knowing that I call the reporter and then check the side effect was called correctly. TDD achieved 😃