Fl4m3Ph03n1x

Fl4m3Ph03n1x

Background

I have a small app with a pool manager, a couple workers and a registry.
For the sake of learning Mox, I made a small test that uses Mox, but it fails for a reason I can’t understand.

Code

This code is a MWE app that starts a registry and a pool:

defmodule MoxIssue.Application do
  use Application

  def start(_type, _args) do
    Supervisor.start_link(
      [
        MoxIssue.ExRegistry,
        MoxIssue.Pool
      ],
      strategy: :one_for_one
    )
  end
end

The pool creates 3 workers and acts as a supervisor:

defmodule MoxIssue.Pool do

  alias MoxIssue.Worker

  require Logger

  @registry Application.fetch_env!(:mox_issue, :registry)

  def start_link do
    children = Enum.map(1..3, &worker_spec/1)

    Logger.debug("Starting supervisor")

    res = Supervisor.start_link(
      children,
      strategy: :one_for_one,
      name: @registry.via_tuple({__MODULE__, __MODULE__})
    )

    Logger.debug("Supervisor started")
    res
  end

  defp worker_spec(worker_id), do:
    Supervisor.child_spec({Worker, {worker_id}}, id: {worker_id})

  def child_spec(_) do
    %{
      id: __MODULE__,
      start: {__MODULE__, :start_link, []},
      type: :supervisor
    }
  end

end

Each Worker is a dummy echo worker that saves a list of messages echoed:

defmodule MoxIssue.Worker do
  use GenServer

  require Logger

  @registry Application.fetch_env!(:mox_issue, :registry)

  defmodule State do
    defstruct messages: [], worker_id: nil
  end

  def start_link({worker_id}) do
    Logger.debug("Starting worker #{inspect worker_id}")

    res = GenServer.start_link(
      __MODULE__,
      %State{worker_id: worker_id},
      name: @registry.via_tuple({__MODULE__, worker_id})
    )

    Logger.debug("Worker registers OK")
    res
  end

  def echo(msg, id) do
    [{pid, _val}] = @registry.lookup({__MODULE__, id})
    GenServer.call(pid, {:echo, msg})
  end

  def init(state), do: {:ok, state}

  def handle_call({:echo, msg}, _from, state) do
    Logger.info(msg)
    new_state = %State{state | messages: [msg] ++ state.messages}
    {:reply, :ok, new_state}
  end
end

Nothing extraordinary here. The most complex piece of code is the Registry, which I took from Elixir in Action and slightly modified for this example. Because Mox really wants you to have contracts, here is the contract used and it’s implementation:

defmodule MoxIssue.ProcessRegistry do
  @type via_tuple_type  ::
    {:via, module, {module, {module, any}}} |
    {:via, module, {module, {module, any}, any}}
  @type module_key :: {module, any}

  @callback lookup(module_key)                  :: [{pid, any}]
  @callback via_tuple(module_key)               :: via_tuple_type
  @callback via_tuple(module_key, any)          :: via_tuple_type
end
defmodule MoxIssue.ExRegistry do
  @behaviour MoxIssue.ProcessRegistry

  alias MoxIssue.ProcessRegistry

  ########
  # API  #
  ########

  @impl ProcessRegistry
  def lookup({_module, _id} = key), do: Registry.lookup(__MODULE__, key)

  @impl ProcessRegistry
  def via_tuple({_module, _id} = key), do:
    {:via, Registry, {__MODULE__, key}}

  @impl ProcessRegistry
  def via_tuple({_module, _id} = key, value), do:
    {:via, Registry, {__MODULE__, key, value}}

  @spec start_link() :: {:ok, pid} | {:error, any}
  def start_link, do:
    Registry.start_link(keys: :unique, name: __MODULE__)

  #############
  # Callbacks #
  #############

  @spec child_spec(any) :: Supervisor.child_spec
  def child_spec(_) do
    Supervisor.child_spec(
      Registry,
      id: __MODULE__,
      start: {__MODULE__, :start_link, []}
    )
  end
end

Test

The test is rather useless. I am trying to replicate an issue with another app we have, so this is the smallest working example I could come up with:

defmodule MoxIssueTest do
  use ExUnit.Case

  import Mox

  alias MoxIssue.RegistryMock
  alias MoxIssue.ExRegistry
  alias MoxIssue.Worker

  # Make sure mocks are verified when the test exits
  setup :verify_on_exit!

  describe "create/1" do

    test "returns an application ready to use with default values" do

      stub_with(RegistryMock, ExRegistry)

      {:ok, _pid} = Worker.start_link({1})
    end

  end
end

This test fails with:

no expectation defined for MoxIssue.RegistryMock.via_tuple/1 in process pid<0.163.0>

Which I don’t understand. The whole reason for using stub_with is exactly because I don’t want to expect anything, nor do i want to assert that function X was called Y times. I just want to replace the Mock with an implementation and have it run.

Why am I getting this error?

Showing Posts 1 to 5

sjoconnor

sjoconnor

You will probably need to use Mox’s global mode. Look here: Mox — Mox v1.2.0

axelson

axelson

Scenic Core Team

Is the code available to view?

peerreynders

peerreynders

Try

mix test --no-start

I wonder whether it’s a timing issue - i.e. the pool starting before the test is run to register the stub.

Fl4m3Ph03n1x

Fl4m3Ph03n1x OP

Been there, done that, didn’t work =(

This is actually the full code. I have however also created a GitHub repo for your convenience :smiley:

https://github.com/Fl4m3Ph03n1x/mox_issue

Nothing works if I use --no-start because mox won’t even launch :frowning_face:

woohaaha

woohaaha

What was the solution for this?

— All posts loaded —

Where Next? Top

Trending in Questions Top

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
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
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
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
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
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
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews