That, of course, is just one particular style. Others are possible and in use. Frankly, if your tests are small, you don’t need to read up and down. If your tests are large, you need to make them smaller no matter what order or style you use.
That’s because most of these mocks mock two things at once - the module’s API, and a (GenServer) process collaborator. In production code that is mockable, I often found that I’d both have a module (the actual implementation I’m talking against) and a pid (the GenServer process that’s essentially the instance of the implementation). For example, when talking to MySQL:
{:ok, pid} = Mysqlex.Connection.start_link(username: "test", database: "test", password: "test", hostname: "10.0.3.82")
Mysqlex.Connection.query(pid, "CREATE TABLE posts (id serial, title text)")
In this case, the Mysqlex.Connection
is the (mockable) module, and pid
the instance I want to talk to. Say that I use a sort of dependency injection style, I’d pass {Mysqlex.Connection, pid}
to a module that has the database as a dependency.
In fact, I got to the pattern in Simpler because mocking a naked module is trivial - if I test code that just requires some behaviour, I can simply do:
test "some test" do
defmodule SomeMock do
def my_method(args), do: "42"
end
assert 42 == MyCodeUnderTest.test_this(SomeMock, args)
end
which isn’t much harder than using ex_mock
to do essentially the same if my quick stroll through the readme has me on the right path. Note that - again, if I understood ex_mock
correctly - the above style will only mock the exact interface that MyCodeUnderTest
needs, and as such serves as specification/documentation about this particular collaboration, more than a mock that just has a generated stub for every module. In essence, when you generate a dummy module with a list of public methods from some other module, you’re mocking that module’s implementation instead of the required interface your code under test needs from this particular collaborator. That’s usually not the best way to mock.
So Simpler, in essence, tries to tackle the harder bit of mocking something that’s close to an object in Elixir - a combination of behaviour (the module) and state (the pid). A lot of such interactions can be seen in my Raft code (I’ve made it a point to develop Simpler alongside “actual code” I’m writing, so it fulfills just the needs of my test code), like here - the persistence part of Raft is mocked and the mock, concisely, specifies how the interaction with the database should ensue. Note that given the widespread occurrence of the {module, pid}
pattern once you start making the module
bit of collaborator mockable, I very often pass it on as a single argument and only unpack the tuple when I need to make an actual call in production code, like for example here - it’s almost ugly enough to warrant some macro magic but I haven’t crossed my personal pain threshold ;).
Hope that background explanation helps. And +1 on helping to build proper testing patterns - meck
and relatives that hack global module names are a scourge that should be wiped from the earth! async: true
is our best path to sane testing.