If you have seen Guardians of the Galaxy, you might remember the scenes where Chris Pratt has his Walkman on, shutting him off from the surrounding chaos. This is what Walkman does, for your test suite.
Elixir already has some good testing libraries, so let’s discuss a couple of them and figure out where Walkman fits in.
Mox - From Plataformatec, Mox is a library that allows you to define mocks based on behaviours. It supports concurrency, so asynchronous testing is still possible.
test "invokes add and mult" do
|> expect(:add, fn x, y -> x + y end)
|> expect(:mult, fn x, y -> x * y end)
assert MyApp.CalcMock.add(2, 3) == 5
assert MyApp.CalcMock.mult(2, 3) == 6
Mock - This library allows you to mock out parts of the system ad-hoc. It applies mocks globally, so does not support asynchronous testing.
test "mock functions with multiple returns" do
"http://example.com" -> "<html>Hello from example.com</html>"
"http://example.org" -> "<html>example.org says hi</html>"
assert HTTPotion.get("http://example.com") == "<html>Hello from example.com</html>"
assert HTTPotion.get("http://example.org") == "<html>example.org says hi</html>"
Walkman - Inspired by Ruby’s VCR, Walkman wraps a module and records function calls to that module and can replay them back later.
test "MyModule" do
Walkman.use_tape "MyModule1" do
assert :ok = calls_my_module()
Technically, Walkman is a stubbing library, not a mocking library. Walkman records the input and output to a stubbed module to “tapes”, which are then replayed on subsequent test runs. This frees you from the drudgery of generating mock objects by generating stubs for you automatically, and it also reduces the coupling of your tests to your code, since you can simply generate new stubs when the code changes.
Walkman works best when you use dependency injection. Let’s say you have a module
MyModule. You can generate a stub with a single line of code:
Walkman.def_stub(MyModuleWrapper, for: MyModule)
Then you can configure your application to use
MyModuleWrapper in the test environment:
config :my_app, my_module: MyModule
config :my_app, my_module: MyModuleWrapper
Now, instead of using
MyModule directly, use
Application.get_env(:my_app, :my_module) in your application code.
That’s it! Now you can write tests like this:
test "walkman test" do
Walkman.use_tape("walkman test") do
### your test here
The first time you run a test successfully, Walkman will record a test fixture. The next time you run that same test, the test fixture will be used, so your test will be isolated from whatever external service you were using.
Walkman has some nice features to make testing as frictionless as possible. One of these is that Walkman will automatically re-record your tapes if a module used in one of those tapes changes. So if you have a test that calls into
MyModule has changed, then Walkman will detect this and re-record the tape.
Walkman also supports asynchronous testing out of the box. By default, a tape will only be accessible from the process that called
Walkman.use_tape/3. You can share the tape with other arbitrary processes by using
test "can share tape with another process" do
Walkman.use_tape "share_tape" do
test_pid = self()
Walkman.share_tape/2 accepts a pid as second argument, so you can share a tape with any other process. The second argument also has a default value of
self(), so if you are running this from the process you would like to access the tape from, then you can leave it.
Sometimes you might need to globally stub a module. This can’t be used with asynchronous tests. To do this you simply add
global: true when calling
test "global tape" do
Walkman.use_tape "global_tape", global: true do
Walkman supports an integration mode that can be turned on by setting
test/test_helper.exs for example). In this mode, no tapes will be used and all function calls will be forwarded to the actual module. I like to run these tests once a day to make sure changes in dependent services haven’t broken my application.
Walkman was inspired by Ruby’s VCR, but with the following important differences:
My hope is that Walkman finds a niche in Elixir’s constellation of testing libraries and that someone out there finds it useful!
Get the ball rolling on your new project, fill out the form below and we'll be in touch quickly.
By: Derek Kraan / 2020-09-03
By: Derek Kraan / 2020-04-23
By: Derek Kraan / 2019-12-06
By: Derek Kraan / 2019-07-22
By: Derek Kraan / 2019-05-20
By: Derek Kraan / 2019-05-06