Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add multiprocess support to local mocking #654

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

jbsf2
Copy link

@jbsf2 jbsf2 commented Feb 9, 2024

Not sure whether this might interest you ...

This PR enables local mock adapters to be "seen" not just within the ExUnit test process, but also within processes spawned by the ExUnit test process. No code changes are required within tests that use Tesla.Mock.

This is accomplished by adding the ProcessTree library to mix.exs, and invoking ProcessTree.get/1 from within Tesla.Mock.pdict_get/0.

Via ProcessTree, code running in child processes can access data stored in the process dictionaries of parent processes, in most circumstances (as discussed further below).

As used here, the result is that processes spawned by the ExUnit test pid can "see" the local mock adapter, but other tests and their child processes cannot. async: true is preserved.

Limitations

ProcessTree is able to return a meaningful answer in most real-world circumstances. For example, when the processes in question are running in a supervision tree, ProcessTree will always be able to return an answer. Beyond that, things depend on factors including:

  • The OTP major version the code is running under. (OTP 25 introduced new functionality for tracking process ancestors.)
  • Whether ancestor processes are still alive
  • Whether the given process and its ancestors were started via raw spawn or were instead started as Tasks, Agents, GenServers or Supervisors

Further discussion can be found here: https://saltycrackers.dev/posts/how-to-get-the-parent-of-an-elixir-process/

Detection/messaging of error circumstances

Helpful error messaging is the trickiest part of this PR.

As was the case prior to this PR, if Mock.call() fails to find an adapter when the code is running within the ExUnit test process, we display the same error message as before:

          There is no mock set for process #{inspect(self())}.
          Use Tesla.Mock.mock/1 to mock HTTP requests.

          See https://github.com/teamon/tesla#testing

If, however, the code calling Mock.call() is not running within the ExUnit test process, that can mean one of three things:

  1. A mock has not been set up at all
  2. The test spawns or manages process in a nonstandard or unusual way that's incompatible with ProcessTree, and Mock.call() has been called within such a process.
  3. Mock.call() has been called within a process not spawned by the ExUnit test pid.

We're unable to distinguish between these situations, so the error messaging is a bit awkward - I've given it my best shot for your review.

@jbsf2 jbsf2 changed the title Add multiprocess support to local mocking feat: add multiprocess support to local mocking Feb 9, 2024
@tomekowal
Copy link

This is gold! 🥇 I recently learned about ProcessTree library and was figuring out if we can use it with Tesla (our Phoenix controllers schedule Oban jobs to make further http calls). AFAIU, Oban spawns jobs as children of the test process in testing mode. With this PR, I could simply change mock_global to mock and async: false to async: true and speed up tests with very little effort!

This commit enables local mock adapters to be "seen" not just
within the ExUnit test process, but also within processes spawned
by the ExUnit test process. No code changes are required within
tests that use Tesla.Mock.

This is accomplished by adding the ProcessTree library to mix.exs,
and invoking ProcessTree.get from within `Tesla.Mock.pdict_get/0`.

Via ProcessTree, code running in child processes can access data
stored in the process dictionaries of parent processes.

As used here, the end result is that processes spawned by the
ExUnit test pid can "see" the local mock adapter, but other tests
and their child processes cannot. `async: true` is preserved.
The old documentation could have been interpreted to mean that only
direct children of the current process can access the provided
local mock.

The new documentation makes it clear that the local mock is available
to both direct and indirect children of the current process.
@jbsf2 jbsf2 force-pushed the multiprocess-local-mocking branch from a617e45 to 0cb4716 Compare March 24, 2024 16:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants