Skip to content

Add warning to stub docs #168

@pdgonzalez872

Description

@pdgonzalez872

Hi @josevalim, hope you are doing well! As usual, thanks for Mox and all the work you do ❤️

I was wondering if we could update the docs on the stub/3 function. After working in a few large Elixir codebases, I’ve noticed a recurring pattern: lingering stub/3 calls that can safely be removed from tests without changing behavior.

From what I've seen, folks reach for stub/3 because it's an overloaded term in testing, much like mock (as shown in that article you wrote back in the day -> https://dashbit.co/blog/mocks-and-explicit-contracts, best in class by the way). Folks are well meaning, but the end result seems to always the same:

  • The test includes a stub/3 that never gets used after refactors
  • When removed, nothing fails
  • More importantly, there’s a missed opportunity to use expect/3, which in most cases would have been the right tool

So far, the reasons for this happening has been mostly because:

  • The term “stub” feels familiar from other libraries and languages
  • Many users don’t immediately notice or understand expect/3 as the verification-based alternative and how powerful it is

Once I show how a certain stub call can be removed and mention expect/3, it kinda clicks for them.

Have you seen this? Would this diff be of value in your opinion? I have this commited and can push it up if you think it adds value.

diff --git a/lib/mox.ex b/lib/mox.ex
index 2ae708b..2bba59f 100644
--- a/lib/mox.ex
+++ b/lib/mox.ex
@@ -597,6 +597,17 @@ defmodule Mox do
       stub(MockWeatherAPI, :get_temp, fn _loc -> {:ok, 30} end)

   `stub/3` will overwrite any previous calls to `stub/3`.
+
+   > #### ⚠️  Warning {: .warning}
+   >
+   > `stub/3` does **not verify that a call occurred**.
+   > If the production code stops calling the stubbed function
+   > (for example, after a refactor), the test will still pass.
+   >
+   > This can lead to unused stubs lingering in tests and a
+   > false sense of coverage. Use `expect/3` instead when you
+   > want to ensure the function is actually invoked, which is
+   > the preferred approach in most cases.
   """
   @spec stub(mock, atom(), function()) :: mock when mock: t()
   def stub(mock, name, code)

Resulting in this:

Image

Thanks!

Paulo

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions