Laravel unit tests - changing a config value depending on test method

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company
# Laravel Unit Tests: Managing Configuration State for Dynamic Scenarios Testing conditional logic based on application configuration is a common requirement in complex applications. When you have optional features—like an account verification flow—controlled by a config value, your tests must accurately reflect the behavior under both enabled and disabled states. The challenge often lies not just in setting the configuration, but ensuring that the test environment remains perfectly isolated between different test methods. This post dives into why manipulating configuration within individual test methods can lead to unpredictable results, and demonstrates the robust way to handle dynamic configuration changes in your Laravel unit tests. ## The Pitfall of Dynamic Configuration Setting You are running into a classic problem related to **test isolation**. In a Laravel testing environment, the application container holds state, including configuration values, which is loaded from disk (the `config` files) and potentially modified during the test execution. When you use `$this->app['config']->set(...)` inside a specific test method, you are modifying the *current* state of that request or test run. While this works for simple scenarios, it can lead to subtle failures if subsequent tests rely on the default loaded configuration, or if the setup/teardown hooks aren't perfectly managing the container reset. The reason you might observe inconsistent behavior is often related to how Laravel caches configuration and how your testing framework manages request lifecycles. If a test modifies the config and that change isn't fully isolated before the next test runs, you get state leakage. ## The Solution: Leveraging Test Setup for Configuration The most reliable and cleanest approach in Laravel testing is to manage application state *before* the action being tested occurs, using the built-in setup methods (`setUp`, `beforeEach`). This ensures that every test starts from a known, clean baseline. Instead of setting the configuration inside each specific test method, set it once in your test setup. This isolates the configuration state for all tests that follow, making your tests more deterministic and easier to debug. Here is how you can restructure your example to achieve reliable testing: ```php use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; class RegistrationTest extends TestCase { use RefreshDatabase; /** * Setup the configuration state before each test runs. */ protected function setUp(): void { parent::setUp(); // Set a default state, or handle toggling based on the specific test context later. // For this example, we will control it per test method explicitly below. } public function test_UserProperlyCreated_WithVerificationDisabled() { // 1. Explicitly set the desired config for THIS test only. config(['auth.verification.enabled' => false]); $this->visit(route('frontend.auth.register.form')); $this->type('Test', 'name'); $this->type('test@example.com', 'email'); $this->type('123123', 'password'); $this->type('123123', 'password_confirmation'); $this->press('Register'); $this->seePageIs('/'); $this->see(trans('auth.registration.complete')); } public function test_UserProperlyCreated_WithVerificationEnabled() { // 2. Explicitly set the configuration for this test. config(['auth.verification.enabled' => true]); $this->visit(route('frontend.auth.register.form')); $this->type('Test', 'name'); $this->type('test@example.com', 'email'); $this->type('123123', 'password'); $this->type('123123', 'password_confirmation'); $this->press('Register'); $this->seePageIs('/'); $this->see(trans('auth.registration.needs_verification')); } } ``` ### Why This Approach is Superior By using the global `config()` helper (which interacts with the service container) directly within each test method, you are explicitly telling Laravel what state to use for that specific request lifecycle. While setting it in `setUp()` would set a default for *all* tests, setting it directly where needed gives you maximum control over the scenario you are testing. Furthermore, ensuring your repository or model tests behave correctly when reading this configuration is crucial. Remember that robust application design, as promoted by frameworks like Laravel, relies on clear separation between data (models), business rules (services/controllers), and environment settings (config). When writing tests, ensure you are testing the *logic* layer effectively, regardless of how the data was loaded from the configuration file itself. ## Conclusion Testing dynamic application behavior requires careful state management. While manipulating the service container via `$this->app['config']` works, always prioritize test isolation. For configuring specific scenarios within a feature test (like your registration flow), setting the config directly before interacting with the route ensures that the test accurately simulates the environment you are targeting. By mastering these testing patterns, you ensure that your Laravel application remains reliable and predictable as you scale up your features.