Error executing unit testing with laravel to load custom settings file - php

When I run the service from browser it works, but if i execute the test file i get this error:
1) Api\Domain\Tests\ServiceTest::testSetUp
RuntimeException: A facade root has not been set.
/WWW/api/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:210
/WWW/api/src/Api/Infrastructure/Domain/Model/SolrBaseRepository.php:30
/WWW/api/src/Api/Infrastructure/Domain/Model/SolrBaseRepository.php:30
/WWW/api/src/Api/Domain/Tests/ServiceTest.php:80
File confguration: config/solr.php
return array(
'endpoint' => array(
'localhost' => array(
'host' => '192.168.1.100',
'port' => 8080,
'path' => '/solr/clients/',
)
)
);
Solr base repository:
use Illuminate\Support\Facades\Config;
abstract class SolrBaseRepository
protected $client;
/**
* SolrBaseRepository constructor.
*/
public function __construct(){
$this->client = new \Solarium\Client(Config::get('solr'));
}
}
if I change
$this->client = new \Solarium\Client(Config::get('solr'));
by
$this->client = new \Solarium\Client(array(
'endpoint' => array(
'localhost' => array(
'host' => '192.168.1.100',
'port' => 8080,
'path' => '/solr/clients/',
)
)
));
The test works.
ServiceTest: Api/Domain/Tests/ServiceTest.php
class ServiceTest extends \PHPUnit_Framework_TestCase{
public function testSetUp()
{
$this->setUp();
$this->solrServicesRepository = New SolrServicesRepository();
}
}
I have tried to resolve it adding from this page to my ServiceTest.php:
use \Illuminate\Container\Container as Container;
use \Illuminate\Support\Facades\Facade as Facade;
/**
* Setup a new app instance container
*
* #var Illuminate\Container\Container
*/
$app = new Container();
$app->singleton('app', 'Illuminate\Container\Container');
/**
* Set $app as FacadeApplication handler
*/
Facade::setFacadeApplication($app);
But this does not work for mi.

It's been a while, but I found the answer as I got stuck with the same problem.
In your test, you need to call the parent setup function, as it initializes:
class ExampleTest extends TestCase
{
public function setUp()
{
parent::setUp();
$this->faker = Faker\Factory::create();
// other lines of code that you need
}
public function testWithFacades()
{
//now it works
}
}

Related

Integrate doctrine in slim 4 using php-di

Trying to use doctrine with slim 4 and php-di I don't get it running with autowire.
Following my setup:
index.php
$definitions = [
'settings' => [
'doctrine' => [
'dev_mode' => true,
'cache_dir' => __DIR__.'/../var/cache/doctrine',
'metadata_dirs' => [__DIR__.'/../src/Domain/'],
'connection' => [
'driver' => 'pdo_mysql',
'host' => 'webdb',
'port' => 3306,
'dbname' => 'db',
'user' => 'user',
'password' => 'pass',
]
]
],
EntityManagerInterface::class => function (ContainerInterface $c): EntityManager {
$doctrineSettings = $c->get('settings')['doctrine'];
$config = Setup::createAnnotationMetadataConfiguration(
$doctrineSettings['metadata_dirs'],
$doctrineSettings['dev_mode']
);
$config->setMetadataDriverImpl(
new AnnotationDriver(
new AnnotationReader,
$doctrineSettings['metadata_dirs']
)
);
$config->setMetadataCacheImpl(
new FilesystemCache($doctrineSettings['cache_dir'])
);
return EntityManager::create($doctrineSettings['connection'], $config);
},
UserRepositoryInterface::class => get(UserRepository::class)
then my repository:
class UserRepository extends \Doctrine\ORM\EntityRepository implements UserRepositoryInterface {
public function get($id){
$user = $this->_em->find($id);
...
}
}
Currently I get the follwoing error message:
"Doctrine\ORM\Mapping\ClassMetadata" cannot be resolved: Parameter $entityName of __construct() has no
value defined or guessable
Full definition:
Object (
class = Doctrine\ORM\Mapping\ClassMetadata
lazy = false
...
can somebody tell me how to solve that issue respectively is there any other maybe cleaner/easier way to integrate doctrine using php-di?
Update
Referring to the hint that ClassMetadata can't be autowired I changed the structure as follows:
index.php
$definitions = [
EntityManager::class => DI\factory([EntityManager::class, 'create'])
->parameter('connection', DI\get('db.params'))
->parameter('config', DI\get('doctrine.config')),
'db.params' => [
'driver' => 'pdo_mysql',
'user' => 'root',
'password' => '',
'dbname' => 'foo',
],
'doctrine.config' => Setup::createAnnotationMetadataConfiguration(array(__DIR__."/src/core/models/User"), true),
...
userservice/core/models/User.php:
namespace userservice\core\models;
use userservice\core\exceptions\ValidationException;
use \DateTime;
use ORM\Entity;
/**
* #Entity(repositoryClass="userservice\infrastructure\repositories\UserRepository")
*/
class User extends Model{
/**
* #Column(type="string", length="50")
* #var string
*/
private $name;
...
And the userservice/infrastructure/UserRepository.php:
namespace userservice\infrastructure\repositories;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\ORMException;
use Doctrine\ORM\ORMInvalidArgumentException;
use userservice\core\models\User;
use userservice\core\repositories\UserRepositoryInterface;
use userservice\infrastructure\repositories\Repository;
class UserRepository extends Repository implements UserRepositoryInterface {
private $_repository;
/**
*
* #param EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager) {
parent::__construct($entityManager);
$this->_repository = $entityManager->getRepository('User'); // OR $this->entityManager->find('User', 1);
}
Now I'm getting the following error in UserRepository construct (getRepository):
Uncaught Doctrine\Persistence\Mapping\MappingException: Class 'User' does not exist in C:\projects\microservices\user-service\vendor\doctrine\persistence\lib\Doctrine\Persistence\Mapping\MappingException.php
How can I get doctrine find the entities?

How can I access mongodb database connection variable in model with zend framework 3

I have succeed to connect mongodb database with zend framework 3 directly without using any module. I have accessed that connection in controller by using factory. But I want to access this in every modules controllers and models i.e I want to access it globally. How can I access "$this->db" globally. Where to write the "connect" method to access that variable globally. I am new to zend framework 3 and if any one have any solution that will be very helpful to proceed with my project. I am sharing the files I have used.
global.php
use Zend\Session\Storage\SessionArrayStorage;
use Zend\Session\Validator\RemoteAddr;
use Zend\Session\Validator\HttpUserAgent;
return [
'config' => [
'driver' => 'mongodb',
'host' => '127.0.0.1',
'port' => 27017,
'username' => 'test',
'password' => 'test',
'dbname' => 'test',
'connection_string'=> sprintf('mongodb://%s:%d/%s','127.0.0.1','27017','test')
],
// Session configuration.
'session_config' => [
// Session cookie will expire in 1 hour.
'cookie_lifetime' => 60*60*1,
// Session data will be stored on server maximum for 30 days.
'gc_maxlifetime' => 60*60*24*30,
],
// Session manager configuration.
'session_manager' => [
// Session validators (used for security).
'validators' => [
RemoteAddr::class,
HttpUserAgent::class,
]
],
// Session storage configuration.
'session_storage' => [
'type' => SessionArrayStorage::class
],
];
UserControllerFactory.php
<?php
namespace Application\Controller;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
use Application\Service\AuthManager;
/**
* This is the factory for UserController. Its purpose is to instantiate the
* controller.
*/
class UserControllerFactory implements FactoryInterface{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null){
$config = $container->get('config');
$db_config = $config['config'];
$authManager = $container->get(AuthManager::class);
$authService = $container->get(\Zend\Authentication\AuthenticationService::class);
return new UserController($db_config, $authManager, $authService);
}
}
UserController.php
<?php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\JsonModel;
use Zend\View\Model\ViewModel;
use Zend\Crypt\Password\Bcrypt;
class UserController extends AbstractActionController{
private $db;
private $authManager;
private $authService;
/**
* Constructor.
*/
public function __construct($config, $authManager, $authService){
$this->connect($config);
$this->authManager = $authManager;
$this->authService = $authService;
}
/**
* Connection.
*/
private function connect($config){
try{
if ( !class_exists('Mongo')){
echo ("The MongoDB PECL extension has not been installed or enabled");
return false;
}
$connection= new \MongoClient($config['connection_string'],
array('username'=>$config['username'],'password'=>$config['password']));
return $this->db = $connection->selectDB($config['dbname']);
}catch(Exception $e) {
return false;
}
}
}

Laravel 5 Unit Test - Call to a member function connection() on null

I tried creating a unit test for the relationships between my User and Shop models, however when I run vendor\\bin\\phpunit this error(s) are thrown, I have no idea about this since I'm a newbie in unit testing. I tried to run my code on my controller to see if the relationship actually works, and fortunately it is working as expected, but not when run in phpunit. What have I done wrong for this phpunit not to work with Models?
Fatal error: Uncaught Error: Call to a member function connection() on null in E:\projects\try\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php:1013
Stack trace:
E:\projects\try\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php(979): Illuminate\Database\Eloquent\Model::resolveConnection(NULL)
This is my UserTest.php
<?php
namespace Tests\Unit;
use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use App\User;
use App\Shop;
class UserTest extends TestCase
{
protected $user, $shop;
function __construct()
{
$this->setUp();
}
function setUp()
{
$user = new User([
'id' => 1,
'first_name' => 'John',
'last_name' => 'Doe',
'email' => 'JohnDoe#example.com',
'password' => 'secret',
'facebook_id' => null,
'type' => 'customer',
'confirmation_code' => null,
'newsletter_subscription' => 0,
'last_online' => null,
'telephone' => null,
'mobile' => null,
'social_security_id' => null,
'address_1' => null,
'address_2' => null,
'city' => null,
'zip_code' => null,
'signed_agreement' => 0,
'is_email_confirmed' => 0
]);
$user = User::find(1);
$shop = new Shop([
'id' => 1,
'user_id' => $user->id,
'name' => 'PureFoods Hotdog2',
'description' => 'Something that describes this shop',
'url' => null,
'currency' => 'USD'
]);
$user->shops()->save($shop);
$shop = new Shop([
'id' => 2,
'user_id' => $user->id,
'name' => 'PureFoods Hotdog',
'description' => 'Something that describes this shop',
'url' => null,
'currency' => 'USD'
]);
$user->shops()->save($shop);
$this->user = $user;
}
/** #test */
public function a_user_has_an_id(){
$user = User::find(1);
$this->assertEquals(1, $user->id);
}
/** #test */
public function a_user_has_a_first_name()
{
$this->assertEquals("John", $this->user->first_name);
}
/** #test */
public function a_user_can_own_multiple_shops()
{
$shops = User::find(1)->shops()->get();
var_dump($this->shops);
$this->assertCount(2, $shops);
}
}
It seems, this error is caused by this line of code:
$user->shops()->save($shop); - this code actually works when run in my sample routes or Controller but is throwing errors when run in phpunit
User.php
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
protected $guarded = [ 'id' ];
protected $table = "users";
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* returns the Shops that this user owned
*/
public function shops()
{
return $this->hasMany('App\Shop');
}
}
Shop.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Shop extends Model
{
protected $guarded = [ 'id' ];
protected $table = "shops";
/**
* returns the Owner of this Shop
*/
public function owner()
{
return $this->belongsTo('App\User');
}
}
Any help will be very much appreciated. Thanks!
First of all, setUp() is called before every test, so you shouldn't call it from within the constructor
Second of all, you should call the parent::setUp() in your setUp() to initialize the app instance.
One more reason
check if the test class is extending use Tests\TestCase; rather then use PHPUnit\Framework\TestCase;
Laravel ships with the later, but Tests\TestCase class take care of setting up the application, otherwise models wont be able to communicate with the database if they are extending PHPunit\Framework\TestCase.
Example:
<?php
class ExampleTest extends TestCase
{
private $user;
public function setUp()
{
parent::setUp();
$this->user = \App\User::first();
}
public function testExample()
{
$this->assertEquals('victor#castrocontreras.com', $this->user->email);
}
}
a solution
use PHPunit\Framework\TestCase
Put this
use Tests\TestCase
You are assigning the id '2' to both shops.
Assigning is should not be neccessary since I assume the shop table id field is an autoincrement field.
Also look into database factories in the Laravel docs, it will simplify things for you.
That can be happens when you try to read database from dataprovider function. Like me tried, yes. The work way:
protected static $tariffs_default;
protected function setUp(): void {
parent::setUp ();
if (!static::$tariffs_default){
static::$tariffs_default = DB::...;
}
}
// in dataprovider we use string parm, named as our static vars
public static function provider_prov2(){
return [
[".....", [ 'tariffs'=>'tariffs_default'] ],
];
}
// then, in test we can ask our data by code:
public function testSome(string $inn, string $ogrn, $resp_body){
if ( $ta_var = Arr::get( $resp_body, 'tariffs' ) ){
Arr::set($resp_body, 'tariffs', static::$$ta_var );
}
$this->assert...
}
Cause of the problem: you can't use Laravel Models functionality from code called in the constructor of Class UserTest - even though you put the code in the method "setUp", you unnecessarily called it from the constructor. SetUp is called by phpUnit without you needing to do it in the constructor.
When the UserTest constructor has run, the Laravel Bootstrap code has not yet been called.
When the UserTest->setUp() method is called, the Laravel Bootstrap code HAS been run, so you can use you Models etc.
class UserTest extends TestCase
{protected $user, $shop;
function __construct()
{
$this->setUp(); // **THIS IS THE PROBLEM LINE**
}
function setUp()
{
$user = new User([....
Try composer dump-autoload
~Regards

Extending Config breaks application

Here is my code:
// Yoda namespace
namespace Yoda\Application\Config\Feature;
// use zend config
use Zend\Config\Config;
// CacheConfig class
class CacheConfig extends Config
{
/**
* Default cache type for now
*
* #var string
*/
const DEFAULT_CACHE_TYPE = 'filesystem';
/**
* Default cache ttl for now
*
* #var integer
*/
const DEFAULT_CACHE_TTL = 3600;
/**
* Constructor. Creates config data for caching
*/
public function __construct()
{
$config=[
'name'=> static::DEFAULT_CACHE_TYPE,
'options' => [
'ttl' => static::DEFAULT_CACHE_TTL,
'cache_dir' => '/var/www/html/yoda/data/cache'
]
];
parent::__construct($config,true);
}
}
When I use this code the application breaks and says The localhost page isn't working however when I just pass the config array into a standard Zend Config object it works fine.
Here's my usage code:
$config=[
'name'=> 'filesystem',
'options' => [
'ttl' => 3600,
'cache_dir' => '/var/www/html/yoda/data/cache'
]
];
//works fine
$configCache = new Config($config);
//breaks
$configCache = new CacheConfig();
Not sure whats wrong here.
It's because in the Config class the constructor loads a static instance of itself. WHen I did this:
public function __construct()
{
$config=[
'name'=> static::DEFAULT_CACHE_TYPE,
'options' => [
'ttl' => static::DEFAULT_CACHE_TTL,
'cache_dir' => yoda::registry('cache_dir')
]
];
$this->allowModifications = true;
foreach ($config as $key => $value) {
if (is_array($value)) {
$this->data[$key] = new Config($value, $this->allowModifications);
} else {
$this->data[$key] = $value;
}
}
}
It seems to work when I replace it with Config
Instead of modifying zend config class you can do following in your configCache constructor. When config class will call cache class with array you pass the control back to config class with received array. It will then set the config object properly.The error is because of Static Bindings.
/**
* Constructor. Creates config data for caching
*/
public function __construct( $arr = [])
{
$config=[
'name'=> static::DEFAULT_CACHE_TYPE,
'options' => [
'ttl' => static::DEFAULT_CACHE_TTL,
'cache_dir' => '/var/www/html/yoda/data/cache'
]
];
if (count($arr) > 0)
{
parent::__construct($arr,true);
}
else
{
parent::__construct($config,true);
}
}
$configCache = new CacheConfig();
print_r($configCache);

How to use database for mail settings in Laravel

I'd like to keep users away from editing configuration files, so I've made web interface in admin panel for setting up Mail server, username, password, port, encryption..
I was working well in Laravel 4.2, but now when the app has been rewritten into Laravel 5, an error occurs:
Class 'Settings' not found in <b>F:\htdocs\app\config\mail.php</b> on line <b>18</b><br />
For this purpose I've created a service provider, made a facade, put them in config/app.php, Settings::get('var')/Settings::set('var') work perfectly, but not for mail settings.
config/mail.php:
<?php return array(
'driver' => Settings::get('mail_driver'),
'host' => Settings::get('mail_host'),
'port' => Settings::get('mail_port'),
'from' => array('address' => Settings::get('mail_from_address'), 'name' => Settings::get('mail_from_name')),
'encryption' => Settings::get('mail_encryption'),
'username' => Settings::get('mail_username'),
'password' => Settings::get('mail_password'),
'sendmail' => Settings::get('mail_sendmail'),
'pretend' => false,
);
config/app.php:
'providers' => [
...
'App\Providers\SettingsServiceProvider',
...
'aliases' => [
...
'Settings' => 'App\Custom\Facades\Settings',
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Custom\Settings;
class SettingsServiceProvider extends ServiceProvider {
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
//
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
$this->app->singleton('settings', function()
{
return new Settings;
});
}
}
<?php namespace App\Custom;
use App\Setting;
class Settings {
public function get($var) {
try{
$setting = Setting::first();
} catch(exception $e)
{
return false;
}
return $setting->$var;
}
public function set($var, $val) {
try{
$setting = Setting::first();
$setting->$var = $val;
$setting->save();
} catch(exception $e)
{
return false;
}
return true;
}
}
<?php
namespace App\Custom\Facades;
use Illuminate\Support\Facades\Facade;
class Settings extends Facade {
protected static function getFacadeAccessor() { return 'settings'; }
}
Any ideas how to implement Laravel mail settings using database?
To archive this I created CustomMailServiceProvider by extending Illuminate\Mail\MailServiceProvider so as to overwrite this method:
protected function registerSwiftTransport(){
$this->app['swift.transport'] = $this->app->share(function($app)
{
return new TransportManager($app);
});
}
Here is the complete solution
I created CustomMailServiceProvider.php in app\Providers
namespace App\Providers;
use Illuminate\Mail\MailServiceProvider;
use App\Customs\CustomTransportManager;
class CustomMailServiceProvider extends MailServiceProvider{
protected function registerSwiftTransport(){
$this->app['swift.transport'] = $this->app->share(function($app)
{
return new CustomTransportManager($app);
});
}
}
I created CustomTransportManager.php in app/customs directory -
NB: app/customs directory doesn't exist in default laravel 5 directory structure, I created it
namespace App\Customs;
use Illuminate\Mail\TransportManager;
use App\Models\Setting; //my models are located in app\models
class CustomTransportManager extends TransportManager {
/**
* Create a new manager instance.
*
* #param \Illuminate\Foundation\Application $app
* #return void
*/
public function __construct($app)
{
$this->app = $app;
if( $settings = Setting::all() ){
$this->app['config']['mail'] = [
'driver' => $settings->mail_driver,
'host' => $settings->mail_host,
'port' => $settings->mail_port,
'from' => [
'address' => $settings->mail_from_address,
'name' => $settings->mail_from_name
],
'encryption' => $settings->mail_encryption,
'username' => $settings->mail_username,
'password' => $settings->mail_password,
'sendmail' => $settings->mail_sendmail,
'pretend' => $settings->mail_pretend
];
}
}
}
And finally, I replaced 'Illuminate\Mail\MailServiceProvider', in config/app.php with 'App\Providers\CustomMailServiceProvider',
I have added
$this->app['config']['services'] = [
'mailgun' => [
'domain' => $settings->mailgun_domain,
'secret' => $settings->mailgun_secret,
]
];
to CustomTransportManager __construct() to include mailgun API credentials that I'm using as mailing service
I configured as mentioned, however got the following error. While I tried your code found that from Laravel 5.4 share method is deprecated and instead informed to use singleton.
Call to undefined method Illuminate\Foundation\Application::share()
here is the below method using singleton instead using share method:
protected function registerSwiftTransport(){
$this->app->singleton('swift.transport', function ($app){
return new CustomTransportManager($app);
});
}
#DigitLimit , method share() has been dropped since Laravel 5.4. I had to work-around this problem using other methods, and I am not sure they are perfect. Here is my registerSwiftTransport() method in CustomMailServiceProvider class.
Firstly, we need to determine if code is not executed while calling app through command line: "if(strpos(php_sapi_name(), 'cli') === false)". If we don't check that and don't prevent setting new params in this case, Artisan will throw us errors in command line. Secondly, we need to get settings from database somehow. I did it using my method getSettingValue(), where first argument is setting key, and second argument is default value if setting is not found. As you see, I assigned settings to $this->app['config']['mail'].
After that, I used singleton() method:
protected function registerSwiftTransport(){
if (strpos(php_sapi_name(), 'cli') === false) {
$this->app['config']['mail'] = [
'driver' => Setting::getSettingValue('mail_driver', '****'),
'host' => Setting::getSettingValue('mail_host', '****'),
'port' => Setting::getSettingValue('mail_port', 25),
'from' => [
'address' => Setting::getSettingValue('mail_from_address', '****'),
'name' => Setting::getSettingValue('mail_from_name', '****'),
],
'encryption' => Setting::getSettingValue('mail_encryption', '***'),
'username' => Setting::getSettingValue('mail_username', '****'),
'password' => Setting::getSettingValue('mail_password', '****'),
];
}
$this->app->singleton('swift.transport', function ($app) {
return new Illuminate\Mail\TransportManager($app);
});
}

Categories