jward3@email.unc.edu's avatar

Set environment at runtime in phpunit test

I'd like to test a vallidation rule for passwords that has different requirements depending on the environment you're in: anything goes in 'local', but passwords must be 'strong' in production.

The rule:

class Password implements Rule
{
    ...
    public function passes($attribute, $value)
    {
        $pattern = app()->environment('local')
                    ? '/.+/'
                    : '/^(?=\S*[a-z])(?=\S*[A-Z])(?=\S*[\d])(?=\S*[‘~!@#$%\^&\*\(\)\-_=\+\{\[\]\}\\`\/])\S*$/';
        return (bool)preg_match($pattern, $value);
    }
    ...

I have a test that checks the rule in the testing environment:

class PasswordValidationTest extends TestCase
    public function setUp()
    {
        parent::setUp();
        $this->u = factory(\App\User::class)->create();
        $this->rule = new Password();
    }

    /**
     * @test
     */
    public function validates_strong()
    {
        $this->assertFalse($this->rule->passes('password', 'testthislongpassowrd'));
    }


    /**
     * @test
     */
    public function validates_strong()
    {
    // Change the environment
        $this->assertTrue($this->rule->passes('password', 'test'));
    }
}

Now I need to test the rule in the 'local' environment. How can I change the environment returned by app()->environment() for just this test now that putenv has been disabled?

Thanks!

0 likes
5 replies
bobbybouwmann's avatar

So this all sounds like best practices. Why would you have a different rule locally than on production? Your use case is probably so you can probably do the functional testing through your application a bit faster!

However for your tests you only specify a string. So for your test pike a good password and reuse that all the time. There is no reason why you would test your local environment with the and than have a different rule on production in my opinion. That's my 2 cents!

1 like
jward3@email.unc.edu's avatar

It's a valid critique, @bobbybouwmann.

As you thought, the idea was to make functional testing a bit faster, but if I can't write tests both cases it's probably not worth the headache. I suppose the environment-based rule set could potentially confuse other devs working on the project.

Would still be interested to hear about a way to change environment at runtime if anyone knows of one.

anilcancakir's avatar

I think you can use Config facade. Because the environment variables sets config variables. So this .env file supports changing configs for your environment.

If you look the config/app.php file you can show the "env" element. This is a config variable which giving from env function.

    /*
    |--------------------------------------------------------------------------
    | Application Environment
    |--------------------------------------------------------------------------
    |
    | This value determines the "environment" your application is currently
    | running in. This may determine how you prefer to configure various
    | services your application utilizes. Set this in your ".env" file.
    |
    */
    'env' => env('APP_ENV', 'production'),

So you can change the config variable in code. You can use the Config facede for it.

Config::set('app.env', 'production');

I'm not sure but maybe you need to revert this changes when completed the test function.

jward3@email.unc.edu's avatar

Thanks for the input, but unfortunately setting app.env doesn't change what the app thinks the environment is:

.env:

APP_ENV=local

in tinker:

>>> app()->environment();
=> "local"
>>> Config::set('app.env', 'production');
=> null
>>> app()->environment();
=> "local"

Looks like the app is setting the environment directly from the APP_ENV as opposed to the config.

With the exception of writing tests (and maybe not even then) I can't think of a time when change the environment at runtime would be a good idea. I think that was the idea behind getting rid of putenv in the first place.

anilcancakir's avatar
Level 3

Yes I looked the source and i found this environment variables sets only one time when booting but the application has a function for detecting or changing the environment by code

You can use this function by sending callback function.

app()->detectEnvironment(function() { return 'production'; });

Ref: \Illuminate\Foundation\Application::detectEnvironment

7 likes

Please or to participate in this conversation.