Mocking laravel environment in test
Asked Answered
S

5

11

I'm implementing some logic that requires code to behave differently in a production environment.

I want to write a test that asserts this actually happens, but I'm having difficulty mocking the environment.

I've seen it suggested to use putenv('APP_ENV=production'); but it doesn't seem to work.

How can I make the test pass?

use Illuminate\Support\Facades\App;
use Tests\TestCase;

class EnvTest extends TestCase
{
    public function testEnv()
    {
        // This assertion is fine
        $env = App::environment();
        $this->assertEquals('testing', $env);

        putenv('APP_ENV=production');

        // This assertion fails
        $env = App::environment();
        $this->assertEquals('production', $env);
    }
}

Result

Time: 160 ms, Memory: 18.00MB

There was 1 failure:

1) EnvTest::testEnv

Failed asserting that two strings are equal.

--- Expected

+++ Actual

@@ @@

-'production'

+'testing'

Springhalt answered 13/6, 2018 at 18:13 Comment(0)
R
14

I know this question is a year old now, but for those who come looking like I did, this worked for me in 5.8

  public function test_config_env()
  {
    $this->app->detectEnvironment(function() {
      return 'production';
    });
    $this->assertEquals('production', app()->environment()); // pass
  }
Retrorocket answered 20/7, 2020 at 15:59 Comment(2)
Note that this approach will cause POST methods to fail with a 419 error if they need a CSRF token. I suppose this is because the app is booted with a 'testing' environment before the individual test is run and CSRF verification is disabled in testing.Camaraderie
@CorieSlate I'd imagine the kind of low-level thing being patched would throw an exception if application is in production. At least this is what I am using this for.Adrenocorticotropic
H
5
use App;

//...

public function my_awesome_test() 
{
    // Number of times method environment() have to be invoked
    $number_of_calls_to_method = 2

    // Fake, that we have production environment
    App::shouldReceive('environment')
        ->times($number_of_calls_to_method)
        ->andReturn('production');

    // Make here whatever you want in Production environment
}
Hitherto answered 2/12, 2018 at 11:44 Comment(2)
Good answer, but this actually doesnt work. App::environment() still returns something different.Cylix
In Laravel 7.x I'm getting Received Mockery_0_Illuminate_Foundation_Application::offsetGet(), but no expectations were specifiedCamaraderie
O
4

App::environment() doesn't seem yo be reading from the config file!

public function test_config_env()
{
    $this->app['config']->set(['app.env' => 'production']);

    $this->assertEquals('production', $this->app['config']->get('app.env')); // pass
    $this->assertEquals('production', $this->app->environment()); // fail
}

public function test_env()
{
    $this->app['env'] = 'production';

    $this->assertEquals('production', config('app.env')); // fail
    $this->assertEquals('production', $this->app['config']->get('app.env')); // fail

    $this->assertEquals('production', $this->app['env']); // pass
    $this->assertEquals('production', $this->app->environment()); // pass
}

Tested on Laravel v5.3

Oration answered 20/7, 2019 at 10:30 Comment(1)
app()['env'] = 'production' worked, the rest of the mocking methods yielded no results.Schroeder
S
2

I adapted the answer of @staskrak so the behaviour of the mock is exactly like calling App::environment() with or without arguments. It also doesn't throw similar errors like Received Mockery_0_Illuminate_Foundation_Application::offsetGet(), but no expectations were specified

use Illuminate\Support\Arr;
use Illuminate\Support\Facades\App;

protected function mockEnvironment(string $environment)
{
    App::shouldReceive('environment')
        ->withAnyArgs()
        ->zeroOrMoreTimes()
        ->andReturnUsing(function ($args) use ($environment) {
            // Return the current environment if no args are passed
            if (!$args) {
                return $environment;
            }

            // Wrap the args in an array if it's not in array yet
            if (!is_array($args)) {
                $args = Arr::wrap($args);
            }

            // Check if the current environment is in the given args
            return in_array($environment, $args);
        });
    App::partialMock();
}
Scary answered 1/4, 2021 at 6:10 Comment(0)
I
0

You can use detectEnviroment to temporarily change the environment variable:

app()->environment('production') // False

app()->detectEnvironment(fn () => 'production');

app()->environment('production') // True

Italianize answered 26/1 at 15:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.