Test-Driven Development with Phoenix (self-published) (free)

:wave: Hi there,

I’ve been working on a Test-Driven Development with Phoenix book. It teaches TDD and BDD by building a chat app from the outside-in. It’s still a work in progress, but the overall TDD process is clear, and the application that we build throughout the book is fully functional. So, I believe people can already benefit from it.

The book is freely available online at tddphoenix.com.

If you’re interested in the process of writing it, I talked some about it in episode 18 of the Thinking Elixir podcast

I hope some find it helpful.

86 Likes

It seems awesome. Thanks for this great free book.

2 Likes

I really like your book and the explanations. The first time I tried to use Wallaby, I had an error "
Wallaby can’t find phantomjs" . So I installed chromedriver and I setup Wallaby using chromedriver. It might be good to update your book to setup Wallaby with phantomjs or chromedriver. Thanks again for your book.

1 Like

This is awesome! I’m learning Phoenix and I currently struggle to put TDD into practice.

I’ll definitely be reading your book. Thank you!

1 Like

Thanks! I definitely need to add a little note about that at the beginning. It’ll be there when I push the next set of changes.

Fantastic! I will try to take a look soon and then share it out as a resource. Thank you!

1 Like

Thank you very much for creating this!

Before parting, let’s take a moment to see what we have accomplished. We’ve written an entire application without opening our browsers. When was the last time you did that?

Very nice :grin:

The one piece of feedback I would like to share after reading through it (some sections thoroughly, some sections skimmingly) is that you do not mention property-based testing. Even if only as a footnote, mentioning it might help people to discover it as one more cool tool for their testing toolbox.

2 Likes

I can’t seem to get this working at all… Even with chromedriver and phantomjs installed.

I am on a mac, I installed Chromedriver and moved it to /usr/local/bin (or you need to add it to your PATH).
And in config.exs, I added

config :wallaby,
  driver: Wallaby.Experimental.Chrome,
  chromedriver: [headless: true]
1 Like

Thanks, adding that to config/test.exs & chromedriver to PATH changed the error message at least!!

$ mix test
Compiling 18 files (.ex)
Generated chatter app
** (MatchError) no match of right hand side value: {:error, {:wallaby, {{:shutdown, {:failed_to_start_child, Wallaby.Experimental.Chrome, {:shutdown, {:failed_to_start_child, Wallaby.Experimental.Chrome.Chromedriver, {:eacces, [{:erlang, :open_port, [{:spawn_executable, "c:/projects/chatter/_build/test/lib/wallaby/priv/run_command.sh"}, [:binary, :stream, :use_stdio, :stderr_to_stdout, :exit_status, {:args, ["c:/tools/chromedriver/chromedriver.exe", "--log-level=OFF", "--port=57042"]}]], [file: 'erlang.erl', line: 2215]}, {Wallaby.Experimental.Chrome.Chromedriver, :init, 1, [file: 'lib/wallaby/experimental/chrome/chromedriver.ex', line: 22]}, {:gen_server, :init_it, 2, [file: 'gen_server.erl', line: 374]}, {:gen_server, 
:init_it, 6, [file: 'gen_server.erl', line: 342]}, {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 249]}]}}}}}, {Wallaby, :start, [:normal, []]}}}}
    test/test_helper.exs:4: (file)
    (elixir 1.10.2) lib/code.ex:917: Code.require_file/2
    (elixir 1.10.2) lib/enum.ex:783: Enum."-each/2-lists^foreach/1-0-"/2
    (elixir 1.10.2) lib/enum.ex:783: Enum.each/2

What version of Wallaby are you using?

In version 0.25 and newer, Chrome stopped being experimental, so the config changes to

config :wallaby, driver: Wallaby.Chrome

If that doesn’t fix the issue, make sure you include the following in your test/test_helper.exs:

{:ok, _} = Application.ensure_all_started(:wallaby)
Application.put_env(:wallaby, :base_url, YourApplication.Endpoint.url)

That your config/test.exs file has server: true and the sql_sandbox flag:

config :chatter, Chatter.Endpoint,
  server: true

config :chatter, :sql_sandbox, true

And then use that flag in your endpoint:

  use Phoenix.Endpoint, otp_app: :chatter

  if Application.get_env(:chatter, :sql_sandbox) do
    plug Phoenix.Ecto.SQL.Sandbox
  end

Hopefully, just one of those steps is missing. Wallaby’s setup docs are pretty good. So check them out if you still have problems.

1 Like

Thanks for helping! I’m on Wallaby 0.26.2 and everything else matches, the only variation is the app name in config/test.exs (Chatter vs ChatterWeb):

config :chatter, ChatterWeb.Endpoint,
  http: [port: 4002],
  server: true

Changing that doesn’t resolve my issue FWIW.

EDIT: I had the wrong Chromedriver (I’d downloaded v87 but my Chrome is v86, so I replaced with v86). Now I get the following:

$ mix test
Compiling 18 files (.ex)
Generated chatter app
19:22:47.955 [error] GenServer Wallaby.Chrome.Chromedriver terminating
** (stop) :eacces
    erlang.erl:2215: :erlang.open_port({:spawn_executable, 'c:/projects/chatter/_build/test/lib/wallaby/priv/run_command.sh'}, [:binary, :stream, :use_stdio, :stderr_to_stdout, :exit_status, {:args, ["chromedriver", "--log-level=OFF", "--port=57004"]}])
    (wallaby 0.26.2) lib/wallaby/chrome/chromedriver/server.ex:96: Wallaby.Chrome.Chromedriver.Server.handle_continue/2
    (stdlib 3.10) gen_server.erl:637: :gen_server.try_dispatch/4
    (stdlib 3.10) gen_server.erl:388: :gen_server.loop/7
    (stdlib 3.10) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Last message: {:continue, :start_chromedriver}

EDIT2: I am guessing it is a Windows permissions issue somehow but don’t know how to go about proving that.

It is definitely a Windows permission error but I don’t know what I can do about it. I tried moving the location of chromedriver.exe but it had no effect.

Running the following in a terminal works.

$ c:/projects/chatter/_build/test/lib/wallaby/priv/run_command.sh chromedriver --log-level=OFF --port=52022
PID: 1665
ChromeDriver was started successfully.

If I try the same within iex then chromedriver can’t be accessed.

$ iex
Interactive Elixir (1.11.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> System.cmd("c:/projects/chatter/_build/test/lib/wallaby/priv/run_command.sh", ~w(chromedriver --log-level=OFF --port=52022))
** (ErlangError) Erlang error: :eacces
    erlang.erl:2272: :erlang.open_port({:spawn_executable, 'c:/projects/chatter/_build/test/lib/wallaby/priv/run_command.sh'}, [:use_stdio, :exit_status, :binary, :hide, {:args, ["chromedriver", "--log-level=OFF", "--port=52022"]}])
    (elixir 1.11.1) lib/system.ex:822: System.cmd/3

Sorry to hear you’re having trouble with this. Unfortunately, I haven’t used a windows computer in a while. So I’m not sure how to set up Chromedriver and make it available in the path so that Wallaby can use it (with correct permissions).

I wonder if there’s someone in the community or from Wallaby’s team who can help. Have you reached out in the Wallaby channel in the Elixir slack? We might be able to find some help there.

This is really amazing work! Well done for getting over the “fear” of sharing with the world. Karma+1 for you. :heart:

5 Likes

:purple_heart: thanks for a great book!

had some feedback but most resolved with your update - spent a lot of time trying to get async tests to work with chromedriver lol - if you switch to selenium you get sandbox errors with the auth/signin controller (which matches the chromedriver timeouts in behaviour eg - when you switch 2 tests to async it fails), so maybe that library isn’t up for the testing, or it’s wallaby :man_shrugging:

maybe add a chapter on DX ergonomics with random tips, like adding mix_test_watch, ex_unit_notifier - and other nice things… eg. having a ./testwatch script that does the mix test.watch --stale etc

excellent intro/guide, and great handling/practical usage of tdd/bdd inner/outer boundaries…

1 Like

This book is exactly what I have needed. I remember when I started developing in Rails there was a TDD book that really helped me fully understand the development cycle. For the longest time it was what I gave credit to my first dev job.

I moved to Elixir/Phoenix and the one thing my applications felt missing compared to my Rails ones was my test suite. I was confident in most of my Rails tests because I 100% TDD, but the tests I was previously creating for elixir were not near as good. This book definitely gave me the perspective I needed for the current Phoenix project I am working on.

I did have problems running the async tests with Wallaby, so I cheated and just limited to number of async tests I can run to 1 with mix test test/chatter_web/features --max-case=1 to make the tests pass. I was using a Mac with chrome driver.

3 Likes

Downloading now. Testing is where I struggle the most. Thanks for this resource!

1 Like

I ended up switching to Linux just to follow this tutorial as I never figured out how to fix those Windows permissions, and I can confirm it was well worth it. This was exactly what I needed - fantastic work.

I’d advise new Elixir/Phoenix coders follow this book even if they never intend to do TDD… it’s such a great way of learning as it helps you grasp the logic (and in particular what common errors mean) much quicker than just trial and error. Although I have a feeling most people will be keen on TDD by the time they are done :slight_smile:

4 Likes

Hi German, thanks for the resource, it looks great!

I just finished chapter 3 and I would like to ask you this; I’ve been interested in learning more about TDD but I’ve been using Phoenix pretty much only to serve GraphQL APIs, have barely touched controllers except as endpoints for webhooks.

I may be head of myself here, but it seems like the type of workflow you use in the book with Wallaby and all would be the best fit for the type of apps I’m building?