diff --git a/README.md b/README.md index a399631..60334d1 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Latest Version on Packagist](https://img.shields.io/packagist/v/pod-point/laravel-configcat.svg?style=flat-square)](https://packagist.org/packages/pod-point/laravel-configcat) ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/pod-point/laravel-configcat/run-tests.yml?branch=main&label=tests) -[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) +[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](https://github.com/Pod-Point/laravel-configcat/blob/main/LICENSE.md) [![Total Downloads](https://img.shields.io/packagist/dt/pod-point/laravel-configcat.svg?style=flat-square)](https://packagist.org/packages/pod-point/laravel-configcat) Implement feature flags within your Laravel application using [ConfigCat](https://configcat.com) cloud service. @@ -17,15 +17,15 @@ composer require pod-point/laravel-configcat ### Publishing the config file -The configuration for this package comes with sensible defaults but there is one mandatory entry you will need to configure, which is your [ConfigCat SDK key](https://app.configcat.com/sdkkey). To do so, publish the configuration file for this package by running: +Next, you should publish the Laravel package configuration file using the `vendor:publish` Artisan command. It will be placed in your application's config directory: ```bash php artisan vendor:publish --provider="PodPoint\ConfigCat\ConfigCatServiceProvider" ``` -You will then be able to specify you SDK `key` within the freshly published configuration file under `config/configcat.php`. +Don't forget to specify your [ConfigCat SDK `key`](https://app.configcat.com/sdkkey) within the freshly published Laravel configuration file under `config/configcat.php`. -See [`config/configcat.php`](config/configcat.php) for more details. +The Laravel configuration for this package comes with sensible defaults. See [`config/configcat.php`](https://github.com/Pod-Point/laravel-configcat/blob/main/config/configcat.php) for more details. ## Usage @@ -43,7 +43,19 @@ $flag = configcat('new_registration_flow'); > **Note:** You can define the actual value of a feature flag to be `bool(true)` or `bool(false)` on ConfigCat but not only, it can also be [a number or a text setting](https://configcat.com/docs/main-concepts/#about-setting-types). -If the feature flag is undefined or something went wrong, `bool(false)` will be returned by default. +If the feature flag is undefined or something went wrong, `bool(false)` will be returned by default, however you can change this by specifying a default value **only when using the Facade or the global helper** to retrieve the feature flag value using: + +```php +use PodPoint\ConfigCat\Facades\ConfigCat; + +$flag = ConfigCat::get('new_registration_flow', true); + +$flag = configcat('new_registration_flow', true); +``` + +You can also globally sepcify a default value from the [`config/configcat.php`](https://github.com/Pod-Point/laravel-configcat/blob/main/config/configcat.php) file. + +:warning: **Only** `boolean`, `string`, `integer` or `float` default value types are supported as these are the only [setting types](https://configcat.com/docs/main-concepts/#about-setting-types) available from ConfigCat. ### Validation rule @@ -116,7 +128,7 @@ The following view content will only be rendered if the feature flag is truthy: The [User Object](https://configcat.com/docs/sdk-reference/php/#user-object) is essential if you'd like to use ConfigCat's [Targeting](https://configcat.com/docs/advanced/targeting) feature. -ConfigCat needs to understand the representation of your users from your application. To do so, you will need to map your user into a `ConfigCat\User` object. This can be done directly from the [`config/configcat.php`](config/configcat.php) file. Here is an example: +ConfigCat needs to understand the representation of your users from your application. To do so, you will need to map your user into a `ConfigCat\User` object. This can be done directly from the [`config/configcat.php`](https://github.com/Pod-Point/laravel-configcat/blob/main/config/configcat.php) file. Here is an example: ```php 'user' => function (\App\Models\User $user) { @@ -135,13 +147,13 @@ use App\Models\User; use PodPoint\ConfigCat\Facades\ConfigCat; $user = User::where('email', 'taylor@laravel.com')->firstOrFail(); -ConfigCat::get('new_registration_flow', $user); +ConfigCat::get('new_registration_flow', $default, $user); ``` This is also applicable for the global helper and the Blade directive: ```php -configcat('new_registration_flow', $user); +configcat('new_registration_flow', $default, $user); ``` ```blade @@ -156,7 +168,7 @@ configcat('new_registration_flow', $user); This package supports native Laravel caching and logging capabilities in order to cache the feature flag values from ConfigCat's CDN as well as log any information when resolving feature flags. We've setup some sensible defaults but various levels of caching and logging can be configured. -See [`config/configcat.php`](config/configcat.php) for more info. +See [`config/configcat.php`](https://github.com/Pod-Point/laravel-configcat/blob/main/config/configcat.php) for more info. ### Test support: mock, fake & overrides @@ -194,9 +206,9 @@ ConfigCat::fake(['new_registration_flow' => true]); When running tests within a browser which doesn't share the same instance of the application, using mocks or fakes is not applicable. This is why we provide some overrides through ConfigCat SDK which will make the client under the hood localhost only and will use a locally generated `json` file in order to read the feature flags for the system under test. -First of all, you will need to make sure to enable `overrides` from [`config/configcat.php`](config/configcat.php). You could also optionally configure the file path for the `json` file if you wish to. The file will be automatically created for you when using overrides. +First of all, you will need to make sure to enable `overrides` from [`config/configcat.php`](https://github.com/Pod-Point/laravel-configcat/blob/main/config/configcat.php). You could also optionally tweak the file path for the `json` file if you wish to. The file will be automatically created for you when using overrides. -Similarly to `ConfigCat::fake()` you can configure some predefined feature flags which will be saved into a `json` file: +Similarly to `ConfigCat::fake()` you can come up with some predefined feature flags which will be saved into a `json` file: ```php use PodPoint\ConfigCat\Facades\ConfigCat; @@ -218,7 +230,7 @@ Please see our [Releases](https://github.com/pod-point/laravel-configcat/release ## Contributing -Please see [CONTRIBUTING](CONTRIBUTING.md) for details. +Please see [CONTRIBUTING](https://github.com/Pod-Point/laravel-configcat/blob/main/CONTRIBUTING.md) for details. ## Credits @@ -229,7 +241,7 @@ Please see [CONTRIBUTING](CONTRIBUTING.md) for details. ## License -The MIT License (MIT). Please see [License File](LICENCE.md) for more information. +The MIT License (MIT). Please see [License File](https://github.com/Pod-Point/laravel-configcat/blob/main/LICENCE.md) for more information. --- diff --git a/config/configcat.php b/config/configcat.php index 3d84cb8..67b565e 100644 --- a/config/configcat.php +++ b/config/configcat.php @@ -8,11 +8,26 @@ |-------------------------------------------------------------------------- | | SDK Key to access your feature flag and setting. Get it from ConfigCat - | Dashboard. This is required in order to use this package. + | Dashboard. This is a hard requirement in order to use this package. + | An invalid key like 'none' could be used when you are willing to + | make the package "offline" and only use default values. */ 'key' => env('CONFIGCAT_KEY', 'none'), + /* + |-------------------------------------------------------------------------- + | Default value + |-------------------------------------------------------------------------- + | + | Here you can define the value which will be returned every time there + | is a problem trying to reach for ConfigCat CDN or when the feature + | flag you are trying to retrieve could not be found. This can be + | overriden when using ConfigCat::get() or configcat() too. + */ + + 'default' => false, + /* |-------------------------------------------------------------------------- | ConfigCat Logging diff --git a/src/ConfigCat.php b/src/ConfigCat.php index a78ce37..ea030c7 100644 --- a/src/ConfigCat.php +++ b/src/ConfigCat.php @@ -14,6 +14,8 @@ class ConfigCat implements FeatureFlagProviderContract { /** @var ConfigCatClient */ protected $configCatClient; + /** @var mixed */ + public $defaultValue = false; /** @var callable|null */ protected $userHandler = null; /** @var string|null */ @@ -21,10 +23,12 @@ class ConfigCat implements FeatureFlagProviderContract public function __construct( ClientInterface $configCatClient, + $defaultValue = false, callable $userHandler = null, string $overridesFilePath = null ) { $this->configCatClient = $configCatClient; + $this->defaultValue = $defaultValue; $this->userHandler = $userHandler; $this->overridesFilePath = $overridesFilePath; } @@ -34,14 +38,16 @@ public function __construct( * will return false if the flag is undefined or if something went wrong. * * @param string $featureKey + * @param mixed|null $default * @param mixed|null $user - * @return bool|string|int + * @return mixed */ - public function get(string $featureKey, $user = null) + public function get(string $featureKey, $default = null, $user = null) { - $user = $this->transformUser($user ?: auth()->user()); + $default = is_null($default) ? $this->defaultValue : $default; + $user = $this->transformUser(is_null($user) ? auth()->user() : $user); - return $this->configCatClient->getValue($featureKey, false, $user); + return $this->configCatClient->getValue($featureKey, $default, $user); } /** diff --git a/src/ConfigCatServiceProvider.php b/src/ConfigCatServiceProvider.php index 531712a..8111a1a 100644 --- a/src/ConfigCatServiceProvider.php +++ b/src/ConfigCatServiceProvider.php @@ -76,8 +76,15 @@ private function registerConfigCatClient() private function registerFacade() { $this->app->singleton('configcat', function ($app) { + $default = $app['config']['configcat.default']; + + if (! is_bool($default) && ! is_string($default) && ! is_int($default) && ! is_float($default)) { + throw new \InvalidArgumentException('The default value can only be of type boolean, string, integer or float.'); + } + return new ConfigCat( $app->make(ClientInterface::class), + $default, $app['config']['configcat.user'], $app['config']['configcat.overrides.enabled'] ? $app['config']['configcat.overrides.file'] diff --git a/src/Contracts/FeatureFlagProviderContract.php b/src/Contracts/FeatureFlagProviderContract.php index 4e0fee7..4fd1960 100644 --- a/src/Contracts/FeatureFlagProviderContract.php +++ b/src/Contracts/FeatureFlagProviderContract.php @@ -6,10 +6,11 @@ interface FeatureFlagProviderContract { /** * @param string $featureKey + * @param mixed|null $default * @param mixed|null $user - * @return bool|string|int + * @return mixed */ - public function get(string $featureKey, $user = null); + public function get(string $featureKey, $default = null, $user = null); /** * @param array $flagsToOverride diff --git a/src/Facades/ConfigCat.php b/src/Facades/ConfigCat.php index 3db813a..a6e12a7 100644 --- a/src/Facades/ConfigCat.php +++ b/src/Facades/ConfigCat.php @@ -6,7 +6,7 @@ use PodPoint\ConfigCat\Support\ConfigCatFake; /** - * @method static bool|string|int get(string $featureKey, $user = null) + * @method static mixed get(string $featureKey, $default = null, $user = null) * @method static void override(array $flagsToOverride = []) * * @see \PodPoint\ConfigCat\ConfigCat diff --git a/src/Support/ConfigCatFake.php b/src/Support/ConfigCatFake.php index 1356d82..4b09933 100644 --- a/src/Support/ConfigCatFake.php +++ b/src/Support/ConfigCatFake.php @@ -39,11 +39,13 @@ public function fake($featureFlags = []): self * feature flag is undefined. * * @param string $featureKey - * @return bool|string|int + * @param mixed|null $default + * @param mixed|null $user + * @return mixed */ - public function get(string $featureKey) + public function get(string $featureKey, $default = null, $user = null) { - $featureValue = $this->featureFlags[$featureKey] ?? false; + $featureValue = $this->featureFlags[$featureKey] ?? ($default ?: $this->provider->defaultValue); if (is_array($featureValue)) { $execution = $this->getCount($featureKey); diff --git a/src/helpers.php b/src/helpers.php index f404031..e8c77b3 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -9,13 +9,12 @@ * If no feature flag is found, false will be returned. * * @param string $featureKey + * @param mixed|null $default * @param mixed|null $user - * @return bool|string|int + * @return mixed */ - function configcat(string $featureKey, $user = null) + function configcat(string $featureKey, $default = null, $user = null) { - return $user - ? ConfigCat::get($featureKey, $user) - : ConfigCat::get($featureKey); + return call_user_func_array([ConfigCat::class, 'get'], func_get_args()); } } diff --git a/tests/Feature/BladeDirectivesTest.php b/tests/Feature/BladeDirectivesTest.php index af98fd8..dfc6911 100644 --- a/tests/Feature/BladeDirectivesTest.php +++ b/tests/Feature/BladeDirectivesTest.php @@ -39,7 +39,7 @@ public function test_it_will_consider_an_unknown_feature_flag_to_be_disabled() public function test_it_will_consider_a_feature_flag_as_a_number_setting_to_be_disabled() { ConfigCat::fake([ - 'enabled_feature' => 123, + 'enabled_feature' => 1234, 'disabled_feature' => false, ]); diff --git a/tests/Feature/ConfigCatTest.php b/tests/Feature/ConfigCatTest.php new file mode 100644 index 0000000..6e7d1e9 --- /dev/null +++ b/tests/Feature/ConfigCatTest.php @@ -0,0 +1,165 @@ +set('configcat.default', 'some_default'); + } + + public function test_it_can_be_configured_to_use_a_default_value() + { + $this->assertEquals('some_default', ConfigCat::get('unknown_feature')); + } + + public function test_it_can_use_laravel_cache() + { + $fakeCachedItem = new CacheItem(); + $fakeCachedItem->config = [ + 'f' => [ + 'some_feature' => [ + 'v' => 'some_cached_value', + 'i' => '430bded3', + 't' => 1, + ], + ], + ]; + + /** @var \Mockery\MockInterface $mockedCacheStore */ + $mockedCacheStore = Mockery::mock(Repository::class); + $mockedCacheStore + ->shouldReceive('get') + ->once() + ->andReturn(serialize($fakeCachedItem)); + + $this->mock('cache', function (MockInterface $mock) use ($mockedCacheStore) { + $mock->shouldReceive('store') + ->once() + ->andReturn($mockedCacheStore); + }); + + $this->assertEquals('some_cached_value', ConfigCat::get('some_feature')); + } + + public function test_it_can_use_laravel_logger() + { + /** @var \Mockery\MockInterface $mock */ + $mock = Mockery::mock(\Psr\Log\LoggerInterface::class); + $mock->shouldReceive('error') + ->with(Mockery::on(function ($message) { + return Str::contains($message, "Evaluating getValue('some_feature')"); + }), Mockery::type('array')); + + if ($this->app->version() >= '5.6.0') { + Log::shouldReceive('channel')->once()->andReturn($mock); + } else { + fclose(STDERR); + $this->instance('log', $mock); + } + + ConfigCat::get('some_feature'); + } + + public function test_the_facade_can_override_feature_flags() + { + config(['configcat.overrides.enabled' => true]); + + ConfigCat::override([ + 'enabled_feature' => true, + 'disabled_feature' => false, + ]); + + $this->assertTrue(configcat('enabled_feature')); + $this->assertFalse(configcat('disabled_feature')); + + $this->assertTrue(File::exists(storage_path('app/features/configcat.json'))); + $this->assertEquals( + '{"flags":{"enabled_feature":true,"disabled_feature":false}}', + File::get(storage_path('app/features/configcat.json')) + ); + } + + public function test_config_cat_client_is_called_when_resolving_feature_flags() + { + $this->mock(ClientInterface::class, function (MockInterface $mock) { + $mock->shouldReceive('getValue')->once(); + }); + + ConfigCat::get('some_feature'); + } + + public function test_a_default_value_can_be_passed_when_resolving_feature_flags() + { + $this->mock(ClientInterface::class, function (MockInterface $mock) { + $mock->shouldReceive('getValue') + ->once() + ->with('foo', 'bar', null); + }); + + ConfigCat::get('foo', 'bar'); + } + + public function test_null_as_a_default_value_will_use_the_default_value_configured_for_the_package() + { + $this->mock(ClientInterface::class, function (MockInterface $mock) { + $mock->shouldReceive('getValue') + ->once() + ->with('foo', 'some_default', null); + }); + + ConfigCat::get('foo', null); + } + + public function test_the_user_handler_can_be_used_when_resolving_feature_flags() + { + $this->mock(ClientInterface::class, function (MockInterface $mock) { + $mock->shouldReceive('getValue') + ->once() + ->with('some_feature', false, \Mockery::on(function (\ConfigCat\User $user) { + return $user->getIdentifier() === '456' + && $user->getAttribute('Email') === 'foo@baz.com'; + })); + }); + + $user = new \Illuminate\Foundation\Auth\User(); + $user->id = 456; + $user->email = 'foo@baz.com'; + + ConfigCat::get('some_feature', false, $user); + } + + public function test_the_user_handler_will_use_the_logged_in_user_by_default() + { + $this->mock(ClientInterface::class, function (MockInterface $mock) { + $mock->shouldReceive('getValue') + ->once() + ->with('some_feature', false, \Mockery::on(function (\ConfigCat\User $user) { + return $user->getIdentifier() === '789' + && $user->getAttribute('Email') === 'foo@foo.com'; + })); + }); + + $user = new \Illuminate\Foundation\Auth\User(); + $user->id = 789; + $user->email = 'foo@foo.com'; + + $this->actingAs($user); + + ConfigCat::get('some_feature', false); + } +} diff --git a/tests/Feature/Facades/ConfigCatTest.php b/tests/Feature/Facades/ConfigCatTest.php deleted file mode 100644 index 978d5dc..0000000 --- a/tests/Feature/Facades/ConfigCatTest.php +++ /dev/null @@ -1,78 +0,0 @@ - true]); - - ConfigCat::override([ - 'enabled_feature' => true, - 'disabled_feature' => false, - ]); - - $this->assertTrue(configcat('enabled_feature')); - $this->assertFalse(configcat('disabled_feature')); - - $this->assertTrue(File::exists(storage_path('app/features/configcat.json'))); - $this->assertEquals( - '{"flags":{"enabled_feature":true,"disabled_feature":false}}', - File::get(storage_path('app/features/configcat.json')) - ); - } - - public function test_config_cat_client_is_called_when_resolving_feature_flags() - { - $this->mock(ClientInterface::class, function (MockInterface $mock) { - $mock->shouldReceive('getValue')->once(); - }); - - ConfigCat::get('some_feature'); - } - - public function test_the_user_handler_can_be_used_when_resolving_feature_flags() - { - $this->mock(ClientInterface::class, function (MockInterface $mock) { - $mock->shouldReceive('getValue') - ->once() - ->with('some_feature', false, \Mockery::on(function (\ConfigCat\User $user) { - return $user->getIdentifier() === '456' - && $user->getAttribute('Email') === 'foo@baz.com'; - })); - }); - - $user = new \Illuminate\Foundation\Auth\User(); - $user->id = 456; - $user->email = 'foo@baz.com'; - - ConfigCat::get('some_feature', $user); - } - - public function test_the_user_handler_will_use_the_logged_in_user_by_default() - { - $this->mock(ClientInterface::class, function (MockInterface $mock) { - $mock->shouldReceive('getValue') - ->once() - ->with('some_feature', false, \Mockery::on(function (\ConfigCat\User $user) { - return $user->getIdentifier() === '789' - && $user->getAttribute('Email') === 'foo@foo.com'; - })); - }); - - $user = new \Illuminate\Foundation\Auth\User(); - $user->id = 789; - $user->email = 'foo@foo.com'; - - $this->actingAs($user); - - ConfigCat::get('some_feature'); - } -} diff --git a/tests/Feature/HelperTest.php b/tests/Feature/HelperTest.php index 8415f44..ae49ebb 100644 --- a/tests/Feature/HelperTest.php +++ b/tests/Feature/HelperTest.php @@ -18,13 +18,24 @@ public function test_global_helper_can_be_used_to_check_if_a_feature_flag_is_ena $this->assertFalse(configcat('some_disabled_feature')); } - public function test_global_helper_returns_false_when_a_feature_flag_does_not_exist() + public function test_global_helper_returns_false_when_a_feature_flag_does_not_exist_by_default() { ConfigCat::fake(['some_feature' => true]); $this->assertFalse(configcat('some_unknown_feature')); } + public function test_global_helper_can_return_a_default_value_when_a_feature_flag_does_not_exist() + { + ConfigCat::fake(['some_feature' => true]); + + $this->assertFalse(configcat('unknown_feature', false)); + $this->assertTrue(configcat('unknown_feature', true)); + $this->assertEquals('foo', configcat('unknown_feature', 'foo')); + $this->assertEquals(1234, configcat('unknown_feature', 1234)); + $this->assertEquals(12.34, configcat('unknown_feature', 12.34)); + } + public function test_global_helper_can_retrieve_a_text_setting() { ConfigCat::fake(['some_feature_as_a_string' => 'foo']); @@ -34,9 +45,13 @@ public function test_global_helper_can_retrieve_a_text_setting() public function test_global_helper_can_retrieve_a_number_setting() { - ConfigCat::fake(['some_feature_as_a_string' => 123]); + ConfigCat::fake([ + 'a_whole_number' => 1234, + 'a_decimal_number' => 12.34, + ]); - $this->assertEquals(123, configcat('some_feature_as_a_string')); + $this->assertEquals(1234, configcat('a_whole_number')); + $this->assertEquals(12.34, configcat('a_decimal_number')); } public function test_global_helper_relies_on_the_facade() @@ -46,14 +61,21 @@ public function test_global_helper_relies_on_the_facade() configcat('some_feature'); } + public function test_global_helper_can_be_used_with_a_default_value() + { + ConfigCat::shouldReceive('get')->once()->with('some_feature', true); + + configcat('some_feature', true); + } + public function test_global_helper_can_be_used_with_a_given_user() { $user = new \Illuminate\Foundation\Auth\User(); $user->id = 123; $user->email = 'foo@bar.com'; - ConfigCat::shouldReceive('get')->once()->with('some_feature', $user); + ConfigCat::shouldReceive('get')->once()->with('some_feature', false, $user); - configcat('some_feature', $user); + configcat('some_feature', false, $user); } } diff --git a/tests/Feature/InvalidDefaultValueTest.php b/tests/Feature/InvalidDefaultValueTest.php new file mode 100644 index 0000000..648cda1 --- /dev/null +++ b/tests/Feature/InvalidDefaultValueTest.php @@ -0,0 +1,22 @@ +set('configcat.default', null); + } + + public function test_null_configured_as_a_default_value_for_the_package_will_throw_an_exception() + { + $this->expectException(\InvalidArgumentException::class); + + ConfigCat::get('foo'); + } +} diff --git a/tests/Feature/Middlewares/CheckFeatureFlagOffTest.php b/tests/Feature/Middlewares/CheckFeatureFlagOffTest.php index df36322..a335631 100644 --- a/tests/Feature/Middlewares/CheckFeatureFlagOffTest.php +++ b/tests/Feature/Middlewares/CheckFeatureFlagOffTest.php @@ -43,7 +43,7 @@ public function test_text_settings_are_treated_like_disabled_features_by_it() public function test_number_settings_are_treated_like_disabled_features_by_it() { - ConfigCat::fake(['some_feature' => 123]); + ConfigCat::fake(['some_feature' => 1234]); Route::post('/foo', function () { return response('Bar!'); diff --git a/tests/Feature/Middlewares/CheckFeatureFlagOnTest.php b/tests/Feature/Middlewares/CheckFeatureFlagOnTest.php index 917fea4..8b39b74 100644 --- a/tests/Feature/Middlewares/CheckFeatureFlagOnTest.php +++ b/tests/Feature/Middlewares/CheckFeatureFlagOnTest.php @@ -43,7 +43,7 @@ public function test_text_settings_are_treated_like_disabled_features_by_it() public function test_number_settings_are_treated_like_disabled_features_by_it() { - ConfigCat::fake(['some_feature' => 123]); + ConfigCat::fake(['some_feature' => 1234]); Route::post('/foo', function () { return response('Bar!'); diff --git a/tests/Feature/Rules/RequiredIfFeatureTest.php b/tests/Feature/Rules/RequiredIfFeatureTest.php index b719802..9b81ea3 100644 --- a/tests/Feature/Rules/RequiredIfFeatureTest.php +++ b/tests/Feature/Rules/RequiredIfFeatureTest.php @@ -75,7 +75,7 @@ public function test_a_field_is_optional_when_a_feature_flag_is_defined_as_a_str public function test_a_field_is_optional_when_a_feature_flag_is_defined_as_a_number() { - ConfigCat::fake(['some_feature' => 123]); + ConfigCat::fake(['some_feature' => 1234]); $validator = Validator::make([ 'foo' => 'bar', diff --git a/tests/TestCase.php b/tests/TestCase.php index e431301..0c801d7 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -43,6 +43,8 @@ protected function getPackageAliases($app) */ protected function getEnvironmentSetUp($app) { + $app['config']->set('cache.default', 'file'); + $app['config']->set('configcat.key', 'testing'); $app['config']->set('configcat.user', function ($user) {