Typing and testing

I think this is the best option at the moment. You have a single interface your code depends on with two implementations of it. One being essentially a wrapper of the official client and another used for testing. The former of which could either be directly monkey patched in, or by decorating the official one and merely delegating the method calls to it. I’m personally a fan of the latter just so you avoid touching code you don’t own at all.

I also think this ties in nicely with how things could be with the idea around [RFC] Standardized Abstractions. Namely the idea that the Github::Client accepts an optional argument of the HTTP::Client that is actually used to make the requests. Under normal usages this would be HTTP::Client, but for purposes of your test, it could be HTTP::MockClient which works something like Testing Guzzle Clients — Guzzle Documentation, allowing you to customize the responses and access the requests made by the underlying client. It could still make sense for your code to depend on some internal interface, but that’s for another discussion.

It’s also entirely possible this could be done without the need for a dedicated interface by virtue of Redesign HTTP::Client to implement missing features · Issue #6011 · crystal-lang/crystal · GitHub of which the Mock connections (for testing) could possibly be a subclass of the built-in HTTP::Client.

IMO this is just a perfect example of how the ideas around dependency injection can be so useful for stuff like this.

2 Likes