Fl4m3Ph03n1x

Fl4m3Ph03n1x

Background

I am trying to create a mock for one of my behaviours. To this end I am using Hammox (a variation of Mox which extends the api).

I have set my application so it gets which module to use from the configuration files, in this case config/test.exs:

config/test.exs

config :my_app, storage: StorageMock

And this is the test file:

test/my_app/application_test.exs

defmodule MyApp.ApplicationTest do
  @moduledoc false

  use ExUnit.Case, async: false
  use MyApp.ConnCase

  import Hammox

  test "client can handle an error response", %{conn: conn} do
    Hammox.defmock(StorageMock, for: MyApp.Storage)

    expect(StorageMock, :get, fn args ->
      assert args == "Chicago"

      # here we decide what the mock returns
      {:ok, %{body: "Some html with weather data"}}
    end)

    get(conn, "~p/api/users/validate")
  end
end

I am trying to test the controller’s endpoint,and I am basically mixing the docs of Hammox (GitHub - msz/hammox: 🏝 automated contract testing via type checking for Elixir functions and mocks · GitHub) with what i can grasp from a Testing controllers guide (Testing Controllers — Phoenix v1.8.8).

Problem

Unfortunately, I think my setup is incorrect, as I get this error:

Compiling 1 file (.ex)
** (Mix) Could not start application my_app: exited in: MyApp.Application.start(:normal, [])
    ** (EXIT) an exception was raised:
        ** (ArgumentError) The module StorageMock was given as a child to a supervisor but it does not exist
            (elixir 1.16.0) lib/supervisor.ex:797: Supervisor.init_child/1
            (elixir 1.16.0) lib/enum.ex:1700: Enum."-map/2-lists^map/1-1-"/2
            (elixir 1.16.0) lib/enum.ex:1700: Enum."-map/2-lists^map/1-1-"/2
            (elixir 1.16.0) lib/supervisor.ex:783: Supervisor.init/2
            (elixir 1.16.0) lib/supervisor.ex:707: Supervisor.start_link/2
            (kernel 9.2) application_master.erl:293: :application_master.start_it_old/4

What am I missing here?
I assume there is some configuration somewhere to tell the VM that StorageMock is something from the library, but I can’t find it.

Showing Posts 1 to 5

rio517

rio517

I am not very familiar with them, but looking at the Mox library and hammox, I don’t think StorageMock is coming from the library. I cannot find any reference to it in the hammox or mox hexdocs.

According to the Mox documentation, you have to define your own custom behavior. My understanding is that you define a real behavior and a mock behavior and then switch to the mock during tests. A good example is in the readme for mox.

https://github.com/dashbitco/mox?tab=readme-ov-file#4-define-the-mock-so-it-is-used-during-tests

Fl4m3Ph03n1x

Fl4m3Ph03n1x OP

StorageMock is my equivalent of DatabaseMock from this README (GitHub - msz/hammox: 🏝 automated contract testing via type checking for Elixir functions and mocks · GitHub).

The behaviour is called Storage, and StorageMock is the Mock for said behaviour.

Both Hammox and Mox work the same way in this regard.

al2o3cr

al2o3cr

Based on the error message, it seems like the application is referencing StorageMock during boot and crashing.

That module isn’t defined until something says Hammox.defmock(StorageMock, for: MyApp.Storage) - you may need to put that call in your test_helper.exs to get it to happen before application boot.

LostKobrakai

LostKobrakai

test_helper.exs is also loaded after application startup. What works is putting the defmock in a compiled file (not a script), e.g. in test/support/mocks.ex or something like that. See the documentation here: Mox — Mox v1.1.0

Fl4m3Ph03n1x

Fl4m3Ph03n1x OP

This is really close to the problem. Turns out my issue was that I was referencing StorageMock at application startup, namely:

defmodule MyApp.Application do
  @moduledoc false

  use Application

  @impl true
  def start(_type, _args) do
   children = 
    [
      MyAppWeb.Telemetry,
      {Phoenix.PubSub, name: MyApp.PubSub},
      {StorageMock, []},
      MyAppWeb.Endpoint
    ]

    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

By removing StorageMock from the list of children (when running tests) the error was fixed.

For those of you curious, you can check in which env the application is running, by using Mix.env(). Thus, you can do something like:

  def start(_type, _args) do
    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children(Mix.env(), opts)
  end

  defp children(:test) do
    [
      MyAppWeb.Telemetry,
      {Phoenix.PubSub, name: MyApp.PubSub},
      MyAppWeb.Endpoint
    ]
  end

  defp children(_other_envs) do
    [
      MyAppWeb.Telemetry,
      {Phoenix.PubSub, name: MyApp.PubSub},
      {RealStorage, ["localhost:5221"]},
      MyAppWeb.Endpoint
    ]
  end

This is the solution I ended up going with, as it means you can make use of the config/test.exs to define what modules you want to use. You can even change modules at runtime by using Application.put_env/3 if you are inclined to do that, which can also come in handy when doing some specific types of tests.

Thanks for the help!

— All posts loaded —

Where Next? Top

Trending in Questions Top

Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews