Mocking Elixir IO.puts
I’m diving head first into Elixir, but not following a linear approach. I’m taking the best experience I have from other programming languages and diving into parts of elixir I need. (So, I will be skipping lots of language details.)
Last post, I got into testing and building executables. The minimum for building out confident code for a client.
This time, let’s get into mocking. Not just any mocking, but mocking out user input and output! Software may interact with a person… and simulating a person through mocking will reduce development time.
Mocking in ExUnit
I love RSpec’s mocking faciities and with the great experience I had with ExUnit, I expected fantastic mocking.
At the time of writing, there wasn’t any mocking built-in to ExUnit. :-/ Shame, the relationship started so well!
I found this mock project by jjh42 and it’s fulfilling my mocking needs.
Mocking other functions is easy, but mocking input and output… even RSpec had a hard time with that! Let’s see how to mock out user input and output with elixir.
IO.puts
The main Elixir method to print to the console or to debug is using IO.puts
.
This is the equivalent of Ruby’s puts
or Python’s print
, C’s printf
.
Where would programming be without this fundmental feedback tool??
The README explains how to mock IO.puts
:
defmodule MyTest do
use ExUnit.Case, async: false
import Mock
test_with_mock "test_name", IO, [:passthrough], [] do
IO.puts "hello"
assert called IO.puts "hello"
end
end
Simple right?
Well, when I started to run tests, I started to see:
vagrant@vagrant:/vagrant/hello$ mix test
Compiling 1 file (.ex)
..hello
.
Finished in 0.2 seconds
3 tests, 0 failures
Randomized with seed 812063
vagrant@vagrant:/vagrant/hello$
Huh? I thought I was mocking out the function. Why is it printing to the screen?
:passthrough
Looks like the :passthrough
option is like RSpec’s and_call_original
, which
is useful when wanting to use the original method in the test.
In this case though, not printing to console would be desirable. I love to have continuous green dots.
To elimiinate output, remove :passthrough
as an option to mock:
test_with_mock "test_name", IO, [], [] do
IO.puts "hello"
assert called IO.puts "hello"
end
results
vagrant@vagrant:/vagrant/hello$ mix test
1) test test_name (HelloTest)
test/hello_test.exs:13
** (UndefinedFunctionError) function IO.puts/1 is undefined or private. Did you mean one of:
* puts/1
* puts/2
code: IO.puts "hello"
stacktrace:
(elixir) IO.puts("hello")
test/hello_test.exs:14: (test)
..
Finished in 0.2 seconds
3 tests, 1 failure
Randomized with seed 319412
vagrant@vagrant:/vagrant/hello$
Weird… so the :passthrough
option was not causing the outpu. So, how I do
mock out IO.puts without on screen output?
Mocking Functions
Looking at other examples on the README, there are functions associated like so:
test "test_name" do
with_mock HTTPotion, [get: fn(_url) -> "<html></html>" end] do
HTTPotion.get("http://example.com")
# Tests that make the expected call
assert called HTTPotion.get("http://example.com")
end
end
Let’s try using a simple function to IO.puts
:
test_with_mock "test_name", IO, [], [puts: fn(_) -> nil end] do
IO.puts "hello"
assert called IO.puts "hello"
end
Running everything again:
vagrant@vagrant:/vagrant/hello$ mix test
...
Finished in 0.2 seconds
3 tests, 0 failures
Randomized with seed 356012
vagrant@vagrant:/vagrant/hello$
Nice, nothing extra on the output line. This took a little more work, but it makes sense.
Conclusion
This is a quick tour of mocking in elixir using
jjh42’s mocking library. Getting IO.puts
mocked
out so there is no on-screen output during test runs.
This time, I covered IO.puts
. Next time, I will go in-depth with IO.gets
and also add some functionality.