commit: 584e4efaafad1b3890e2fc62a3b1debee795d8cb
parent 1e35ea785afc09a50c4d4a9b6e4a4624239ce2bf
Author: Lain Soykaf <lain@lain.com>
Date: Tue, 25 Feb 2025 12:49:10 +0400
mox_testing.md: Update with more information
Diffstat:
1 file changed, 79 insertions(+), 0 deletions(-)
diff --git a/docs/development/mox_testing.md b/docs/development/mox_testing.md
@@ -95,6 +95,85 @@ end
This means that by default, these mocks will behave like their real implementations unless you explicitly override them with expectations in your tests.
+### Understanding Config Mock Types
+
+Pleroma has three different Config mock implementations, each with a specific purpose and different characteristics regarding async test safety:
+
+#### 1. ConfigMock
+
+- Defined in `test/support/mocks.ex` as `Mox.defmock(Pleroma.ConfigMock, for: Pleroma.Config.Getting)`
+- It's stubbed with the real `Pleroma.Config` by default in `DataCase`: `Mox.stub_with(Pleroma.ConfigMock, Pleroma.Config)`
+- This means it falls back to the normal configuration behavior unless explicitly overridden
+- Used for general mocking of configuration in tests where you want most config to behave normally
+- ⚠️ **NOT ASYNC-SAFE**: Since it's stubbed with the real `Pleroma.Config`, it modifies global application state
+- Can not be used in tests with `async: true`
+
+#### 2. StaticStubbedConfigMock
+
+- Defined in `test/support/mocks.ex` as `Mox.defmock(Pleroma.StaticStubbedConfigMock, for: Pleroma.Config.Getting)`
+- It's stubbed with `Pleroma.Test.StaticConfig` (defined in `test/test_helper.exs`)
+- `Pleroma.Test.StaticConfig` creates a completely static configuration snapshot at the start of the test run:
+ ```elixir
+ defmodule Pleroma.Test.StaticConfig do
+ @moduledoc """
+ This module provides a Config that is completely static, built at startup time from the environment.
+ It's safe to use in testing as it will not modify any state.
+ """
+
+ @behaviour Pleroma.Config.Getting
+ @config Application.get_all_env(:pleroma)
+
+ def get(path, default \\ nil) do
+ get_in(@config, path) || default
+ end
+ end
+ ```
+- Configuration is frozen at startup time and doesn't change during the test run
+- ✅ **ASYNC-SAFE**: Never modifies global state since it uses a frozen snapshot of the configuration
+
+#### 3. UnstubbedConfigMock
+
+- Defined in `test/support/mocks.ex` as `Mox.defmock(Pleroma.UnstubbedConfigMock, for: Pleroma.Config.Getting)`
+- Unlike the other two mocks, it's not automatically stubbed with any implementation in `DataCase`
+- Starts completely "unstubbed" and requires tests to explicitly set expectations or stub it
+- The most commonly used configuration mock in the test suite
+- Often aliased as `ConfigMock` in individual test files: `alias Pleroma.UnstubbedConfigMock, as: ConfigMock`
+- Set as the default config implementation in `config/test.exs`: `config :pleroma, :config_impl, Pleroma.UnstubbedConfigMock`
+- Offers maximum flexibility for tests that need precise control over configuration values
+- ✅ **ASYNC-SAFE**: Safe if used with `expect()` to set up test-specific expectations (since expectations are process-scoped)
+
+#### Configuring Components to Use Specific Mocks
+
+In `config/test.exs`, different components can be configured to use different configuration mocks:
+
+```elixir
+# Components using UnstubbedConfigMock
+config :pleroma, Pleroma.Upload, config_impl: Pleroma.UnstubbedConfigMock
+config :pleroma, Pleroma.User.Backup, config_impl: Pleroma.UnstubbedConfigMock
+config :pleroma, Pleroma.Uploaders.S3, config_impl: Pleroma.UnstubbedConfigMock
+
+# Components using StaticStubbedConfigMock (async-safe)
+config :pleroma, Pleroma.Language.LanguageDetector, config_impl: Pleroma.StaticStubbedConfigMock
+config :pleroma, Pleroma.Web.RichMedia.Helpers, config_impl: Pleroma.StaticStubbedConfigMock
+config :pleroma, Pleroma.Web.Plugs.HTTPSecurityPlug, config_impl: Pleroma.StaticStubbedConfigMock
+```
+
+This allows different parts of the application to use the most appropriate configuration mocking strategy based on their specific needs.
+
+#### When to Use Each Config Mock Type
+
+- **ConfigMock**: ⚠️ For non-async tests only, when you want most configuration to behave normally with occasional overrides
+- **StaticStubbedConfigMock**: ✅ For async tests where modifying global state would be problematic and a static configuration is sufficient
+- **UnstubbedConfigMock**: ⚠️ Use carefully in async tests; set specific expectations rather than stubbing with implementations that modify global state
+
+#### Summary of Async Safety
+
+| Mock Type | Async-Safe? | Best Use Case |
+|-----------|-------------|--------------|
+| ConfigMock | ❌ No | Non-async tests that need minimal configuration overrides |
+| StaticStubbedConfigMock | ✅ Yes | Async tests that need configuration values without modification |
+| UnstubbedConfigMock | ⚠️ Depends | Any test with careful usage; set expectations rather than stubbing |
+
## Configuration in Async Tests
### Understanding `clear_config` Limitations