Elixir Patterns for Testing with Mox

When I need mocks in my Elixir tests, I prefer to use the Mox library. Mox follows the principle of mocking-by-explicit contract. The mox readme explains it like this:

  1. No ad-hoc mocks. You can only create mocks based on behaviours
  2. No dynamic generation of modules during tests. Mocks are preferably defined in your test_helper.exs or in a setup_all block and not per test
  3. Concurrency support. Tests using the same mock can still use async: true
  4. Rely on pattern matching and function clauses for asserting on the input instead of complex expectation rules

I’ve found that this works very well with the overall design of the Elixir language. Elixir developers generally prefer explicitly combining functional pieces over any sort of magical hidden state. Some of the other mocking libraries fiddle with your function definitions behind the scenes. I’ve found that this makes test code harder to read and debug.

When you are using Mox, I believe that it helps if you follow certain patterns. I’ve worked on several projects using Mox at this point, and I’ve seen the pain that can come from not establishing those pattern up front. I will summarize my recommendations briefly here before showing some examples:

  1. You should define one facade module which both your application and test code call in almost all cases.
  2. The facade module should delegate all its work to either the true adapter or the mock adapter, depending on the environment.
  3. One piece of configuration should control which adapter is used, and that configuration should be wrapped up in a nice easy to call function.

To boil that down to only one sentence: your application code should not know or care that you are using Mox. If you find your application code caring that it could be mocked out in a test later, you need a new set of abstractions. In fact, most of your test code shouldn’t need to know about the mocks, except where it needs to set function call expectations.

What does the code to do this look like? Let’s say your application uses a weather API and you don’t want to use the real API in your test suite.

Let’s say this is the module your application already has for communicating with the weather API:

defmodule Weather do
  def current_weather(zip_code) do
      # makes a GET request to the API to ask for the current weather in a zip code

We would define a behaviour for a weather adapter:

defmodule WeatherBehaviour do
  @callback current_weather(binary) :: map

We would have the Weather module adopt the behavior and we would rename it so that it can serve as our live api adapter. Later, we’ll tell our application to use the LiveWeather adapter by default.

defmodule LiveWeather
  @behaviour WeatherBehaviour
  def current_weather(zip_code) do
      # makes a GET request to the API to ask for the current weather in a zip code

At this point, we need to create a new Weather module so that all of our application code doesn’t explode. Our new Weather module won’t do much. It will just delegate down to either LiveWeather, or the mock weather adapter as appropriate for the environment:

defmodule Weather do
  defdelegate current_weather(zip), to: WeatherApp.weather_adapter()

and we’ll need to define weather_adapter/0 in our application class:

defmodule WeatherApp do
  def weather_adapter do
      Application.get_env(:weather_app, :weather_api_adapter, LiveWeather)

By default, the application will use LiveWeather. This is good for development and production. However, in the test environment, we need to tell the system to use the mock. In config/test.exs:

config :weather_app, weather_api_adapter: WeatherMock

And we need to actually define our mock! In test/test_helper.exs:

Mox.defmock(WeatherMock, for: WeatherBehaviour)

That’s all the setup. Notice that nothing outside of the Weather module and it’s configuration changed. None of our controllers or other contexts were disturbed in the process.

If we want to write a test for a function that uses our Weather API, we need to tell Mox what function calls to expect and what to return:

defmodule UserTest do
  # standard test boilerplate as before
  test "current_weather/1 gives the current weather for the user" do
    user = %User{zip_code: "19120"}
    |> expect(:current_weather, fn "19120" ->
          # I've heard it is always sunny there
          %{"description" => "clear"}
    assert %{"description" => "clear"} = User.current_weather(user)


30 of the Best People in Software Lost Their Jobs Yesterday

30 of the best people in software lost their jobs yesterday. And I lost my job too. Revelry Labs, where I worked for close to seven years (five of those as VP of Engineering), has downsized from around 70 people to about half that.

I’ve never met such a high-performing group of hyper-competent and flexible problem-solvers. These people are top-of-their-class Engineers, Designers, Product Managers, Marketing folks, Relationship Managers, and more. But ultimately, I think what sets these folks apart is their humanity, openness, good humor, and adaptability. I think any team would be lucky to have them.

I’m still a big believer in Revelry. The team that is remaining there is also full of amazing, hyper-competent, kind, and caring people. I think Revelry is one of the best agencies out there and one of the most underrated. I’ve learned so much from Gerard in the last seven years. I feel a whole slew of contradictory emotions about this right now, but I can say I’m a lot more sad than mad.

I joined as the 2nd Engineering hire. We grew the company from 4 people to almost 70. My role was growing the Engineering team from 2 to over 30. But I don’t care about the growth itself. What I care about is that we managed to grow 10x while remaining open, transparent, flat, democratic, and kind. It’s that at every turn, between any two options, we took the one that was moral, ethical, and humane.

None of these people lost their jobs through any fault of their own. Some of us put ourselves on the list because we refused to let anyone go unless we went first. Some people merely lost the die roll by not being attached to any billable work at the moment when the economy tanked.

I want to talk about a few of these people specifically. I haven’t had a chance to talk to everyone yet, and I didn’t want to mention anyone without express permission. Anyone missing from this list is missing for that reason. I’ll continue to update this list as I catch up with more folks:

John Hawkins is the person who taught me to respect and enjoy WordPress (a framework that I previously loathed) through his transfer of enthusiasm. I’ve seen him twist and bend the framework in ways I didn’t think possible in order to achieve amazing things on extremely short timelines. Need WordPress? John is your guy. You can reach him here.

Ben Rongey has the rare ability to be direct and say what everyone else is thinking, but in a way that puts everyone at ease and points them in the right direction. Even when he was a very new member of the team, he course-corrected me meaningfully multiple times. He’s also a hell of an Engineer who undersells himself. And he’s only going to get much better over the next few years of his career. Need someone who is not just a great Engineer, but can set you straight with kindness and professionalism? That’s Ben. Here is Ben on LinkedIn.

Mary Legendre was the head of our Quality Assurance and Test Engineering group. Her growth over the last five years has been unreal. She completely built and formalized our process for all things QA, both manual and automated. She designed and implemented our entire modern testing framework in Elixir and JavaScript including integration testing, headless browsers, mobile testing, and CI/CD pipelines. Everything. She’s also a natural leader with a style that is all about empathy and professionalism hand-in-hand. You should let Mary build out your QA department. Here is Mary on LinkedIn.

Josh Frank brought so many missing pieces to our puzzle when he joined. I’ve never known anyone who could better balance the demands of our customers, our customer’s customers, and our team against the ugly reality of budgets and timelines. He’s responsible for the excellence of our Product, Process, and Project Management approaches. Need an amazing Product mind? Josh is the one for you. He’s here on LinkedIn and here on Twitter.

Hauwa Aguillard is another one who really helped us to complete our equation. Hauwa is a UX expert. She’s the one who taught us to not just care about user data in the abstract but to make it real and part of every decision we made. She also clearly cares about community and teaching. Every time Hauwa goes to a conference, you know that she’s going to come back and teach the team everything she learned. And whenever she learns something on the job, she finds a way to share it with other UX practitioners in our community. Need someone who will make your UX program best in class? That’s Hauwa. You can find her on LinkedIn here.

Aline Adams is just a Swiss-army knife of competence. We put her through half a dozen different job descriptions in as many years and she was good at every single one of them. We did it because he knew she would handle it. Project Management, on software projects, or marketing campaigns, or whatever? Aline’s got you. Content development? Aline’s got you. Marketing? SEO? Aline’s got you. Etcetc? Aline’s got you. You can find Aline here.

I can also privately connect you with a lot more folks of this caliber, across all disciplines— Engineers, Designers, Growth, Operations, &c &c. You can reach me on Twitter or by email.

I can’t recommend these folks strongly enough. You should hire them. And you should probably do it soon before either Revelry snaps them back up or I figure out how to hire them myself.


On "Time Famine"

I was recently introduced to the concept of “time famine”, which is:

the universal feeling of having too much to do but not enough time to deal with those demands.

I know the feeling. Though the term has been around since 1999 or so, there’s been a spike in discussion lately. The focus of that discussion has been on finding ways to save time, mostly via automation products and delivery services. It’s almost as if there are people with a vested interest in selling you a “solution” to this problem (whether it works or not). I think most of the discussion and most of the proposed solutions entirely miss the point.

It’s natural to look at your todo list and despair. It’s particularly natural to despair when you look at your todo list over the span of days, months, and years and never see the numbers tick down. It’s natural but it’s also wrong.

Here’s the truth that you don’t want to hear: your todo list will be empty when you are dead. When are you going to stop doing laundry? When are you going to stop needing to go to the grocery store? When are you going to stop having stuff to do at work? When are you going to stop having to call your landlord or fix things at home? When are you going to stop pursuing your hobbies? When you are dead. Thus, your todo list will be empty when you are dead and not a second before. This is OK.

Your task list isn’t a flooded basement, it is a drain pipe. You don’t need to be concerned that there is water in there. That’s where the water goes. You need to be concerned if you are constantly putting more in there than can flow out the other end. Be concerned if the pipes are backing up into the house. Stop having an emotional crisis over the task count in some app. Consider how many things you got done this week, and how many new things you added to the list. These numbers, on a long enough time scale, should be about the same. If you are constantly adding twice as many things to the list as you are checking off, you are going to have a bad time.

By all means, use home automation or a delivery service if it makes you happy. They can be a good way to improve your flow. But if you think of your life as a flooded basement, these things will only ever make you feel like your basement is temporarily less flooded.

How can you stop feeling bad about your task pipeline?

  • Cut yourself some slack. We’re surrounded by pressure to do more. It comes from our bosses, productivity advice sites, our hobbies, and even our families. But you are the only person who can decide that you are doing enough.
  • Say no. I became much happier after I realized that there were certain hobbies that I can envy from afar, but cannot personally undertake. There are certain social occasions that I wish I could attend, but I simply cannot while keeping my sanity.
  • Embrace an organizational system that is about flow, not zero. I’m very partial to David Allen’s Getting Things Done methodology, which probably saved me from death by anxiety and depression. That’s a big statement and I mean it.

I’m certainly not immune to feeling “time famine.” The difference with this mindset and these tools is that I feel it less often. When I do, I notice the thoughts and can quash the anxiety.