Laravel phpunit not getting right url - php

I've changed the app.url config value to the correct url (http://testing.local) for testing locally, however when I run my phpunit tests and try to call(), it is trying to query http://localhost instead of the value of app.url. What do I need to do to get phpunit to call the right path?
I know that it is not actually calling the url, just processing it as if it was, but I can't seem to get it to actually work. Could it have something to do with testing.local directly linking to /public instead of /?

If you want to statically specify a root URL for your tests, add this to phpunit.xml:
<env name="APP_URL" value="http://testing.local"/>
Instead, if you want to change the URL dinamically during your tests, from Laravel 5.4 the $baseUrl method doesn't work anymore.
Also, trying to set the url dinamically with \Config:set('app.url', 'http://testing.local') doesn't work either, as it seems that Laravel caches the root url
You can set dynamically a custom URL with:
\URL::forceRootUrl('http://testing.local');

To change testing url of PHPUnit:
Go to /tests/TestCase.php in your Laravel project.
Change next line to url you need:
// The base URL to use while testing the application.
protected $baseUrl = 'http://newurl.com';
Done.

As stated above, you can use Config::get('app.url') anywhere in your laravel code, including your unit-test.
Note: it is recommended to set this value in your .env file so that it can be set specifically for each environment.
When working with config and .env variables, remember to clear the cache for these with the following commands:
php artisan cache:clear
php artisan config:cache

Laravel has a few functions to get url: (1) url()->current() (2) request()->url()
Both of them are using the same source.
In case of phpunit, tests are setting data there from the config
config('app.url')
and config is getting data from .env record APP_URL.
So you have a few options
1)
to set APP_URL in .env file or in phpunit.xml file - in both cases, a the value is only used in unit tests.
BUT you would have only one URL for all your unit tests.
2)
set APP_URL in runtime
but, you need to do it before function setUp() in Tests\TestCase
public function setUp(): void
{
$_ENV['APP_URL'] = 'example.com';
parent::setUp();
}
Because all the initialisation is hidden in setUp function you CANNOT use
config()->set('app.url',.... Before parent::setUp(); would be too early, and after it - would be too late.
BUT you would have only one URL per unit test file.
3)
request()->headers->set('HOST', 'example.com');
you can set it anywhere after setUp() function and it will overwrite .env and config()
No 'BUTs'.

I think the best way is to set the test app url in phpunit.xml configuration file:
<env name="APP_URL" value="some-url.test"/>

To the best of my understanding, Laravel is supposed to use the 'testing' environment variables when you run PHPUnit. However, I am having the same issue that you are. To get it to work I manually set the app url.
$test_url = Config::get('app.url');
Hope this helps anyone who comes across the same issue.

Related

Laravel Environment Variables(without default value passed in method) not working inside Artisan Command file

I am using Laravel 6.x for client's project wherein I have built an API in the Artisan Command for syncing data.
Now client wants the configuration to be discreet, out of main source code and without any fallback values as possible. That means I must define the configs in the .env file and use the env() method without any fallback default values.
This must be possible inside Laravel Artisan command class files, but it is not working as intended when I use the env method in the code as below:
[siteroot]\.env:
APP_ENV=local
[siteroot]\app\Console\Commands\SyncSomeData.php:
use Illuminate\Console\Command;
class SyncSomeData extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'sync:some-data';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
exit(env('APP_ENV','staging1'));
}
}
This always prints staging1 in console and if I use this instead of the given env method, then it prints nothing:
exit(env('APP_ENV'));
As I know and can trust that Laravel is most secure, there must be way to have env method work without fallback value in the command file, can anyone help fulfilling this ?
There are a couple parts to this answer.
Caching Config
The issue in this question is only present if you have cached your config on your local environment. If you have ever run php artisan config:cache or php artisan optimize, you have cached your config.
As a result, laravel will no longer read your .env file and load all values for config and .env from cache.
It is best practice, to not use env() throughout your application but create config files for these values. This will speed up your application in production. If you want to take advantage of config cache, you CAN NOT use env() anywhere but in your config files.
In your local environment, the documentation recommends you should not be caching your configuration, since it will be updated frequently. I think this is good advice with one caveat: env(). If you aren't caching your config on local, but are in production, you won't come across errors like the one that was the impetus for this post in local. If you have a ci pipeline and good testing practices than this hurdle is overcome.
If you stick with cache config in local, every time you update your config files or your .env file you will need to run php artisan config:cache.
Disabling config cache
By running php artisan config:clear or removing /bootstrap/cache/config.php laravel will no longer to attempt to read config from cache, and .env will work outside of config files. If you do this, as I stated in (1) make sure there is a part of your pipeline that will catch these errors, if you are caching config in production.
After more discussion with OP, I will make one more thing clear:
Cache config does not automatically just cache your .env keys so you can access them like config('SOME_CUSTOM_DOT_ENV_KEY'). When using cache config, if you need to access environment variables inside of your application, you must reference them in a config file.
Again, once config cache is enabled,.env becomes useless, but it is used to build the cache.
So if you in .env say:
GOOGLE_API_TOKEN=xxxx
You would maybe create a config file like:
google.php
return [
'api_token' => env('GOOGLE_API_TOKEN', 'some-default-value'),
];
And in your application, you will only reference config('google.api_token') and never env('GOOGLE_API_TOKEN').

Should I be using env() outside my config files?

I stumbled across this https://laravel.com/docs/5.4/configuration#configuration-caching in the documentation and it confused me a bit.
When I want an environment variable I use the env() function to return what I want. According to the above link it says I should be using the config() function instead to ensure that on production I am accessing the values through a cache.
e.g. These both return the same thing
env('APP_URL')
vs
config('app.url')
So should I be using config() or env() inside my app?
I assume that if I add a new env variable I will also need to update my config files?
You should never use env() in the code directly. It's a good practice to use config(). In config files use env() to get the data from .env file.
In this case, you can easily override config values at runtime or during testing.
You also can use config caching.
To give your application a speed boost, you should cache all of your configuration files into a single file using the config:cache Artisan command.
Another reason is described in the docs:
You should typically run the php artisan config:cache command as part of your production deployment routine. If you execute the config:cache command during your deployment process, you should be sure that you are only calling the env function from within your configuration files.

Laravel 5 configuration - environments and overriding

I installed fresh Laravel 5 copy.
My detectEnvironment function is defined this way:
$app->detectEnvironment(function()
{
return 'local';
return getenv('APP_ENV') ?: 'production';
});
In config\local I've created database.php file:
<?php
return [
'nothing' => 'new',
];
I run php artisan clear-compiled.
My index method of WelcomeController is defined this way:
public function index(Application $app)
{
echo $app->environment();
var_dump($app['config']['database']);
//echo $app['config']['database'];
return view('welcome');
}
Application was imported this way: use Illuminate\Foundation\Application;
The result I get is:
local array(1) { ["nothing"]=> string(3) "new" }
whereas I would expect Laravel to cascade config file with production one (with the default config\database.php file.
The strange thing is that even if I comment the line return 'local'; run again php artisan clear-compiled it shows:
production array(1) { ["nothing"]=> string(3) "new" }
so it seems it always loads database.php file content (this one from local folder) and overrides main database.php file. It works fine again when I change this file name to for example aaa.php.
Is it a bug or maybe environment configuration shouldn't be stored inside config directory? But if not, where should they be store? I don't know if it's a bug or a feature so if anyone knows more about it, please give me a clue.
Although in documentation for Laravel dev (5.0) there is info that configuration will cascade it's not true. I have tested it about 2 weeks ago and it seems at the moment the only way to have different values for environments is using ENV file where you put custom values for current environment. Putting settings in directories won't work as it used to work however it's possible it will change or maybe has been already changed for last 2 weeks.
There's a package that brings the cascading config system back to Laravel 5.
Disclaimer: I am the author.
For me it looks like defect in Laravel 5 dev branch. I was able to work around by adding manual environment detection and configuration. This code does it.
'default' => $app->environment()=='testing'?'sqlite':'mysql',
It is easy to configure Laravel 5 environment.
Open your root application folder and find ".env.example",
Copy and rename into ".env",
Please fit ".env" file into your environment,
If you use GIT, make sure you don't push this file to your GIT repository.
For 'complete explanation', I write this configuration here.
Edited;
I quote from the developer in His github repository readme.md file;
phpdotenv is made for development environments, and generally should
not be used in production. In production, the actual environment
variables should be set so that there is no overhead of loading the
.env file on each request. This can be achieved via an automated
deployment process with tools like Vagrant, chef, or Puppet, or can be
set manually with cloud hosts like Pagodabox and Heroku.
So, you need to create ".env" file per machine and don't use ".env" file in your production server.

Laravel Environment

I am very confused about the environment in Laravel 4.2. I have setup my environment folders in the /config folder. I then added the following:
$env = $app->detectEnvironment(array(
'production' => array('my-server-name'),
'local' => array('my-local-name')
));
where my-server-name and my-local-name where taken from the terminal by running hostname.
When I do this however, no matter where I run the code, the environment is always production. What am I doing wrong?
I think you're not using detectEnvironment correctly. In this function, YOU are supposed to return the environment used, based on a config file, or an external environment variable - not get the environment. If you look at the documentation, you'll see some samples on how to use detectEnvironment properly, with both config file, or external variable (e.g. set the environment variable MY_LARVEL_ENV=local at the command line, and access it using $_SERVER['MY_LARAVEL_ENV'] inside the function, to return the proper environment.
in the beginning of a project in laravel could be a really mess with the roots and all that stuff, so I recommend you to follow this tutorial!
https://github.com/JeffreyWay/Laravel-4-Generators
Go to the root of your project on cmd and follow the steps,once your ready, just type:
php artisan generate:scaffold theNameOfYourTable
And say yes!
It will create you all te MVC, Seeders,Standar of your project, and some other awesome staff. ;)

Laravel Config::get() from particular environment (not current)

I'm trying to get a config value from another (not current) environment. I would like to get it calling something like:
$value = Config::get('app.locale', 'default', 'my_environment');
Obviuosly Config::get() takes only 2 params by default, but are there any other functions to get config in neat way, to get what I want? I can't find anything in the API.
An answer to this question How to get config data in laravel in a subfolder happens to be a solution for me as well.
Rather than passing some other parameters to Config::get(), you can pass a path to file in the first parameter. So calls like:
Config::get('local/app.locale');
Config::get('dev/app.locale');
will get config from local, dev, etc. environments, no matter what's your current working environment.
Laravel environments, are for seperating configurations from develop machine and production machine.
If you want on all env. the same config you can use the global config files in app/config/* for individual configuration per env. you use the app/config/<YOUR ENVIRONMENT>.
so to get your app.locale in all environment the same use the app/config/app.php file

Categories