Allow Walkman to play nicely with Mox
I have an external API under test, let's call it Twitter for the sake of the example.
Suppose my real implementation is MyApp.Twitter.LiveClient, and I am using Mox to be able to implement mocks around this API. So I also have a behaviour, MyApp.Twitter.Client, and a stub client MyApp.Twitter.StubClient. I define a mock client with Mox:
Mox.defmock(MyApp.Twitter.MockClient, for: MyApp.Twitter.Client)
In test mode, my app code is configured to use MyApp.Twitter.MockClient, which allows me to enable the stub client:
Mox.stub_with(MyApp.Twitter.MockClient, MyApp.Twitter.StubClient)
And I can also enable the live client:
Mox.stub_with(MyApp.Twitter.MockClient, MyApp.Twitter.LiveClient)
What I'd like to be able to do is have recorded responses when using the live client. So I install Walkman and do this:
Walkman.def_stub(MyApp.Twitter.RecordedClient, for: MyApp.Twitter.LiveClient)
Unfortunately, if I now try to instruct Mox to switch in the recorded client:
Mox.stub_with(MyApp.Twitter.MockClient, MyApp.Twitter.RecordedClient)
I'll get an error, because MyApp.Twitter.RecordedClient doesn't implement the MyApp.Twitter.Client behaviour.
I can work around the problem by creating a wrapper module, which does implement the behaviour, and delegates to MyApp.Twitter.RecordedClient:
defmodule MyApp.Twitter.RecordedClientWrapper do
@behaviour MyApp.Twitter.Client
Enum.each(MyApp.Twitter.Client.behaviour_info(:callbacks), fn {fun, arity} ->
args = Macro.generate_arguments(arity, MyApp.Twitter.Client)
@impl MyApp.Twitter.Client
defdelegate unquote(fun)(unquote_splicing(args)),
to: MyApp.Twitter.RecordedClient
end)
end
And now pass that to Mox:
Mox.stub_with(MyApp.Twitter.MockClient, MyApp.Twitter.RecordedClientWrapper)
It would be nice to have support for this use case baked into Walkman.def_stub. Perhaps something like this:
Walkman.def_stub(MyApp.Twitter.RecordedClient, for: MyApp.Twitter.LiveClient, impl: MyApp.Twitter.Client)
Or perhaps it's even possible for Walkman to notice that MyApp.Twitter.LiveClient implements MyApp.Twitter.Client and act accordingly, I don't know...