Closed ThomasLandauer closed 7 months ago
@ThomasLandauer Can you fix it?
Same here
I have similar issue, it happens because Codeception neither loads environment directly nor uses Symfony's way to do that (specifically, does not include config/bootstrap.php
).
My solution is the following snippet in codeception.yml
:
params:
- .env.test
Maybe it will be better to modify Symfony
module to check version and config/env file layout and automatically load config/bootstrap.php
. Though, that file could contain custom code which might not be desired in testing environment. So, the fix above is a bit awkward but also least risky for now?
the main problem is, that config/bootstrap.php
is not loaded and also that phpunit.xml
or phpunit.xml.dist
also are not considered.
my phpunit.xml.dist
:
...
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/6.5/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="config/bootstrap.php"
>
<php>
<ini name="error_reporting" value="-1" />
<server name="APP_ENV" value="test" force="true" />
...
so phpunit normally loads config/bootstrap.php
and also sets APP_ENV=test
. so a solution could be to parse the phpunit.xml*
and respect the content.
my workaround is to rebuild the behavior of phpunit.xml.dist
with codeception.
params
from codeception.yml
config/bootstrap.php
in codeceptions tests/_bootstrap.php
ad 1.
#params:
# - .env.test # get params from .env.test file
# - env # to get params from environment vars (only real env)
# - .env # get params from .env file
ad 2.
tests/_bootstrap.php
:
// force test environment
$_ENV['APP_ENV'] = 'test';
// load the symfony bootstrap file to ensure correct dotenv handling.
require dirname(__DIR__).'/config/bootstrap.php';
ad 3.
Add this to your codeception.yml to load the new tests/_bootstrap.php you just created - see https://codeception.com/docs/reference/Configuration#Global-Configuration:
bootstrap: _bootstrap.php
bootstrap.php
fileedit: updated comment with comment from @ThomasLandauer https://github.com/Codeception/Codeception/issues/5411#issuecomment-585279655
@c33s solution works for me (Symfony 4.4) - there's just one step missing:
codeception.yml
to load the new tests/_bootstrap.php
you just created - see https://codeception.com/docs/reference/Configuration#Global-Configuration:
bootstrap: bootstrap.php
I'm using Symfony 5 in my current project and I had this issue too.
Adding env.test file to params section worked for me, but it would definitely be better if Symfony module did it automatically.
Including config/bootstrap in _initialize
method works too, but it is a breaking change, so it should be released as 2.0.0 of Symfony module.
An alternative approach is to add .env.test
to codeception.yaml in recipe: https://github.com/symfony/recipes-contrib/blob/master/codeception/codeception/2.3/codeception.yaml
The only reason .env
is currently loaded automatically is that it is set by that recipe file.
Currently I prefer modifying recipe file.
So I wanted to commit my code and realized that env.test
must be committed to git, but my secrets are there.
Comment at the top of .env
says that
# In all environments, the following files are loaded if they exist,
# the latter taking precedence over the former:
#
# * .env contains default values for the environment variables needed by the app
# * .env.local uncommitted file with local overrides
# * .env.$APP_ENV committed environment-specific defaults
# * .env.$APP_ENV.local uncommitted environment-specific overrides
Comment above Dotenv::loadEnv
method says that
- .env.local is always ignored in test env because tests should produce the same results for everyone.
Secrets must go to .env.test.local
file, right?
Then .env.test.local
must be enabled in params
section and if the file doesn't exist codeception run
fails with error that Params file .../.env.test.local not found
.
Updating recipe looks like bad option now and it would be better to include bootstrap.php
Maybe a stupid question, but which secrets do you have in test environment?
Same issue here, isn't possible to pass in testing credentials without hacking the config.
why hack the config? can you explain the problem? do you have "real" secrets in your tests? i have dummy secrets for my tests and the are in .env.test
and commited inside the repo to ensure tests are running everywhere. so what "secrets" are you trying to store? @ThomasLandauer & @dant89
@c33s Yes we do have "real" secrets because we test production APIs and third party API / bundle integrations periodically, I don't think that's the point though... .env.test.local
should be read over .env.test
. Turning this into a discussion about why it's okay to put "real" secrets into git repositories isn't helpful or the right solution.
I tried the approach suggested by @c33s but it doesn't seem to work for me. Controller always uses .env
instead of switching to the .env.test
. I am using Symfony5.
I removed all params:
from codeception.yml
and added bootstrap: bootstrap.php
. I also added $_ENV['APP_ENV'] = 'test';
to tests/bootstrap.php
. var_dump still shows values from .env
.
Any idea what I may be missing ?
@dant89 ok, i get it, you have real secrets in your file, so .env.test.local
(which is not in the repo) is the right location for it. what is the problem/task you want to archive? if you put the secrets in the .env.test.local
file or maybe even in your real environment, they are "safe" there. i use gitlab, which has its own CI, where i have a gitlab-ci.yml
where i can define non-secret env variables for the docker container and in the gitlab gui i can define secret environment variables that are additionally set for the docker container where the tests run. if you have server, which is doing the production "tests" (calling external apis to check them periodically has nothing to do with testing for me, this is monitoring). than you can even deploy a .env.test.local
directly on the server.
my question would be, what are you trying to archive?
@callmebob2016 do you have a minimal example for me? for me it sounds really stange, if you define $_ENV['APP_ENV']=test
that your app is started in dev/prod env.
i think it's a dotenv or php config problem,
symfony initiates the dotenv class in bootstrap.php
:
---snip---
if (is_array($env = @include dirname(__DIR__).'/.env.local.php') && (!isset($env['APP_ENV']) || ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? $env['APP_ENV']) === $env['APP_ENV'])) {
foreach ($env as $k => $v) {
$_ENV[$k] = $_ENV[$k] ?? (isset($_SERVER[$k]) && 0 !== strpos($k, 'HTTP_') ? $_SERVER[$k] : $v);
}
} elseif (!class_exists(Dotenv::class)) {
throw new RuntimeException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.');
} else {
// load all the .env files
(new Dotenv(false))->loadEnv(dirname(__DIR__).'/.env');
}
---snip---
first check if the dotenv is instanced with false
or true
the default parameter was changed from true
to false
as far as i can remember.
use your debugger or just use dump
to see if dotenv ist even loaded. check the content of APP_ENV
.
or it can have something to do with the phpconfig variables_order = EGPCS
see https://www.php.net/manual/en/ini.core.php
can be that your env variables are not set.
general note: stuff defined in .env
is always loaded if not overwritten from .env.test
or .env.test.local
(in this order)
how the dotenv component has changed and how it's working: https://symfony.com/blog/new-in-symfony-4-2-define-env-vars-per-environment https://symfony.com/doc/current/configuration/dot-env-changes.html
btw i am online in slack (symfony & codeception)
@c33s thanks for quick reply.
Please have a look at this example: https://github.com/callmebob2016/env_swap_test
This is basically symfony5 + codeception + phpbrowser. Everything should be as per your original description. However, controller always returns dev
, also when run from Codeception.
@callmebob2016 you are using acceptance tests and not functional tests, also you are using phpbrowser and not the symfony module. so your setup behave differently.
see my PR at your project, i added the symfony module and i am not using phpbrowser. in the config you see, that the symfony module also has the hardcoded value in which environment the kernel should be booted. then the other environment variables should be correctly loaded from your .env.test.local
file
the APP_ENV
is a special variable, which handles all choices afterwards. you should either set them hardcoded in the symfony module or via environment variables in your webserver. if you use a real browser to access the page, the symfony app will boot into the normal environment setup in your project. you can add a index-test.php
where you also force an environment if you don't want to use the symfony module.
edit: for future reference:
you should enable the symfony module for use with your tests in tests/acceptance.suite.yml
and/or tests/functional.suite.yml
and there define the environment:
modules:
enabled:
- Symfony:
url: 'http://localhost/'
app_path: 'src' # specify custom path to your app dir, where the kernel interface is located.
var_path: 'var' # specify custom path to your var dir, where bootstrap cache is located.
environment: 'test' # environment used for load kernel
@Naktibalda
Adding env.test file to params section worked for me, but it would definitely be better if Symfony module did it automatically.
:-1:
Including config/bootstrap in
_initialize
method works too, but it is a breaking change, so it should be released as 2.0.0 of Symfony module.
:+1:
An alternative approach is to add
.env.test
to codeception.yaml in recipe: https://github.com/symfony/recipes-contrib/blob/master/codeception/codeception/2.3/codeception.yaml The only reason.env
is currently loaded automatically is that it is set by that recipe file. Currently I prefer modifying recipe file.
:-1:
Secrets must go to
.env.test.local
file, right?
yes. but secrets in the tests? if you have secrets they should got to real environment variables but as i wrote above, if you use codeception to monitor your production application, it's not testing, its monitoring and then we should write a real prod symfony app (lets call it monitoring), which is doing the calls to the other symfony app (lets call it app-app). not sure if codeception is the right way for it. if codeception is just the tool to easily access the prod pages, i am sure the code of codeception can directly be used in symfony but then it should be called in prod environment.
Then
.env.test.local
must be enabled inparams
section and if the file doesn't existcodeception run
fails with error thatParams file .../.env.test.local not found
.
no, the app environment must be correctly set and the bootstrap.php
of symfony will do the rest.
Updating recipe looks like bad option now and it would be better to include bootstrap.php
:+1:
@c33s
Brilliant, thanks a lot.
One more question: why do you see using acceptance test as incorrect here? I thought (perhaps I am wrong here), that under the hood they are the same code. The difference apart from the conceptual is modules are in their config files. Also I can see in your PR, that both work now. What am I missing ?
@callmebob2016 i absolutly don't see it as incorrect, it's just something i noticed. which test type you use for what is your decision or the "decision" of best practices you are following.
i personally just connect acceptance tests with real remote controlled browser or phantom (which is/may be totally wrong).
https://stackoverflow.com/questions/3370334/difference-between-acceptance-test-and-functional-test
you miss nothing, both work just fine now :)
why hack the config? can you explain the problem? do you have "real" secrets in your tests? i have dummy secrets for my tests and the are in
.env.test
and commited inside the repo to ensure tests are running everywhere. so what "secrets" are you trying to store? @ThomasLandauer & @dant89
@c33s fyi: .env().local for symfony are not only for storing secrets. They are also meant to store settings that are dependent where the tests are run. E.g. paths go obviously also very often into the .env().local config file. Every developer in a team will have e.g. another path to a local folder where certificates/thumbnails/cache-things are stored for dev/test environment when using symfony.
Symfony dropped the config/bootstrap.php
file with either 5.0 or 5.1, since most of the logic was moved to Dotenv
. This is the codeception setup that now works for me when using Sf 5.1:
tests/_bootstrap.php
:
<?php
$_ENV['APP_ENV'] = 'test';
(new Symfony\Component\Dotenv\Dotenv())->bootEnv(dirname(__DIR__).'/.env');
And I'm with the others here: I prefer to have the tests handle .env
the same way Symfony does. This includes using the pre-compiled php environment files as well if necessary. Dotenv now does all that and it just works.
@TavoNiievez Shouldn't this be moved to https://github.com/Codeception/module-symfony/issues too?
@ThomasLandauer yeap. Done. Is #88 the same issue?
@TavoNiievez via slack:
can you please check the current status of issue #99?
to sum things up:
codeception should never automatically load any .env files via params (if a user want to load it, they should load it via manual config). the reason behind this is that symfony is loading the dotenv files in a specific way which also differs between symfony versions. to have a correct behavior it should be always loaded like symfony is loading it. https://symfony.com/blog/improvements-to-the-handling-of-env-files-for-all-symfony-versions https://symfony.com/doc/current/configuration/dot-env-changes.html#updating-my-application
for older symfony versions this means we have to include the bootstrap.php
file where the dotenv loading is done like it's done in https://github.com/Codeception/module-symfony/pull/4
to give a little insignt this is one variant of bootstrap.php
if (is_array($env = @include dirname(__DIR__).'/.env.local.php') && (!isset($env['APP_ENV']) || ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? $env['APP_ENV']) === $env['APP_ENV'])) {
foreach ($env as $k => $v) {
$_ENV[$k] = $_ENV[$k] ?? (isset($_SERVER[$k]) && 0 !== strpos($k, 'HTTP_') ? $_SERVER[$k] : $v);
}
} elseif (!class_exists(Dotenv::class)) {
throw new RuntimeException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.');
} else {
// load all the .env files
(new Dotenv(false))->loadEnv(dirname(__DIR__).'/.env');
}
but this code also differs between different symfony versions. in older versions the dotenv component was initialized without a parameter, then the parameters was added. in the past the dotenv code was inside of the console
and index.php
then it moved into the dotenv component.
so the code in https://github.com/Codeception/module-symfony/pull/4 should be adapted to handle this.
Is there any update on this? Just started trying to use codeception and got stung by this unexpected dotenv behaviour - wasted a few hours trying to figure it out, which almost put me off using codeception altogether...
No updates. I haven't used this module since I posted my comment 3 years ago.
No updates from my side either.
For anyone interested, especially @ThomasLandauer and @c33s I opened a new PR for this: #190
Starting in November 2018, Symfony has changed the way
.env
files are working: https://symfony.com/doc/current/configuration.html#the-env-file-environment-variablesThese changes are not realized in Codeception.
To assert this, I created a controller which just outputs an environment variable:
When I access the url with codeception (
codecept_debug($I->grabPageSource());
), I always get what I have in.env
(should be.env.local
or.env.test
).However, when I open the page in the browser in DEV environment, I get the content of
.env.dev
, when in TEST environment,.env.test
; this is the expected behavior.Details