Laravel 5 Reseeding the Database for Unit Testing Between Tests - database

I start with a seeded database and am trying to reseed the database between unit tests in Laravel 5. In Laravel 4 I understand you could simply use Illuminate\Support\Facades\Artisan and run the commands
Artisan::call('migrate');
Artisan::call('db:seed');
or you supposedly could do:
$this->seed('DatabaseSeeder');
before every test. In Laravel 5 this appears to have been replaced by
use DatabaseMigrations;
or
use DatabaseTransactions;
I have tried using these and have managed to get the tests to migrate the database; however, it doesn't actually reseed the data in the tables. I have read through several forums complaining about this and have tried several different approaches calling these from the TestCase and inside every Test...adding the
$this->beforeApplicationDestroyed(function () {
Artisan::call('migrate');
Artisan::call('migrate:reset');
Artisan::call('db:seed');
DB::disconnect();
});
to the TestCase.php tearDown()...
I have also tried adding
$this->createApplication();
to a method called in every test from TestCase.php
Sometimes it just wipes my tables out completely. Nothing I am finding on Laravel's site or in blogs seems to work. Part of it is probably because I'm probably trying Laravel 4 methods in Laravel 5. Is there any way to do this in Laravel 5?
My code for the testcase.php looks like:
<?php
use Illuminate\Support\Facades\Artisan as Artisan;
class TestCase extends Illuminate\Foundation\Testing\TestCase{
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
protected $baseUrl = 'http://localhost';
public function initializeTests(){
$this->createApplication();
Artisan::call('migrate');
$this->artisan('migrate');
Artisan::call('db:seed');
$this->artisan('db:seed');
$this->seed('DatabaseSeeder');
$this->session(['test' => 'session']);
$this->seed('DatabaseSeeder');
}
public function tearDown()
{
Mockery::close();
Artisan::call('migrate:reset');
$this->artisan('migrate:reset');
Artisan::call('migrate:rollback');
$this->artisan('migrate:rollback');
Artisan::call('migrate');
$this->artisan('migrate');
Artisan::call('db:seed');
$this->artisan('db:seed');
$this->seed('DatabaseSeeder');
DB::disconnect();
foreach (\DB::getConnections() as $connection) {
$connection->disconnect();
}
$this->beforeApplicationDestroyed(function () {
Artisan::call('migrate:reset');
$this->artisan('migrate:reset');
Artisan::call('migrate:rollback');
$this->artisan('migrate:rollback');
Artisan::call('migrate');
$this->artisan('migrate');
Artisan::call('db:seed');
$this->artisan('db:seed');
$this->seed('DatabaseSeeder');
DB::disconnect();
foreach (\DB::getConnections() as $connection) {
$connection->disconnect();
}
});
$this->flushSession();
parent::tearDown();
}
public function getConnection()
{
$Connection = mysqli_connect($GLOBALS['DB_DSN'], $GLOBALS['DB_USERNAME'], $GLOBALS['DB_PASSWORD'], $GLOBALS['DB_DATABASE']);
$this->createDefaultDBConnection();
return $this->Connection;
}
public function createApplication()
{
$app = require __DIR__.'/../bootstrap/app.php';
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
return $app;
}
/**
* Magic helper method to make running requests simpler.
*
* #param $method
* #param $args
* #return \Illuminate\Http\Response
*/
public function __call($method, $args)
{
if (in_array($method, ['get', 'post', 'put', 'patch', 'delete']))
{
return $this->call($method, $args[0]);
}
throw new BadMethodCallException;
}
/**
* Create a mock of a class as well as an instance.
*
* #param $class
* #return \Mockery\MockInterface
*/
public function mock($class)
{
$mock = Mockery::mock($class);
$this->app->instance($class, $mock);
return $mock;
}
}
My Test looks something like
<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Artisan;
class CustomerRegistrationControllerTest extends TestCase
{
use DatabaseMigrations;
protected static $db_inited = false;
protected static function initDB()
{
echo "\n---Customer Registration Controller Tests---\n"; // proof it only runs once per test TestCase class
Artisan::call('migrate');
Artisan::call('db:seed');
}
public function setUp()
{
parent::setUp();
if (!static::$db_inited) {
static::$db_inited = true;
static::initDB();
}
// $this->app->refreshApplication();
$this->artisan('migrate:refresh');
$this->seed();
$this->seed('DatabaseSeeder');
$this->initializeTests();
);
}
public function testSomething()
{
$this->Mock
->shouldReceive('destroy')
->with('1')
->andReturn();
$this->RegistrationController->postRegistration();
// $this->assertResponseStatus(200);
}
}

Just run this:
$this->artisan('migrate:refresh', [
'--seed' => '1'
]);
To avoid changes to the database persisting between tests add use DatabaseTransactions to your tests that hit the database.

Why not create your own command like db:reset.
This command either truncate all your tables or drop/create schema and then migrate.
In your test you then use: $this->call('db:reset') in between your tests

Related

Symfony: How to load all paths from the same Index route (to use dynamic routing in a React SPA)

I'm creating a SPA backed by Symfony and ApiPlatform so I want to always load my main route despite the real path of the URL.
I want something like this:
/**
* {#inheritdoc}
*/
class DefaultController extends Controller
{
/**
* #Route("/*", name="homepage")
*
* #return Response
*/
public function indexAction(): Response
{
// replace this example code with whatever you need
return $this->render('default/index.html.twig');
}
}
In my intentions, also if the URL is something like /path/to/the/spa/page I want to anyway load the DefaultController::indexAction()route.
How to do this? (obviously the provided example doesn't work).
Ok, I've found the solution after an "illumination".
I remembered that there is the possibility to rewrite all URL adding or removing the trailing slash
Reading that article I saw this:
class RedirectingController extends Controller
{
/**
* #Route("/{url}", name="remove_trailing_slash",
* requirements={"url" = ".*\/$"})
*/
public function removeTrailingSlash(Request $request)
{
// ...
}
}
So, to intercept all URL despite the path, my DefaultController::indexAction() becomes this:
class DefaultController extends Controller
{
/**
* #Route("/{url}",requirements={"url"=".*"}, name="homepage")
*
* #return Response
*/
public function indexAction(): Response
{
// replace this example code with whatever you need
return $this->render('default/index.html.twig');
}
}
Now all URL are all handled by DefaultController::indexAction() despite the URL path.
I would recommend you to use symfony's event system instead.
Subscribe to either kernel.request or kernel.router events.
In case of kernel.request you have to overtake the Symfony\Component\HttpKernel\EventListener\RouterListener::onKernelRequest()
which priotiry is 32 (use 33 at least).
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class SpaSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
KernelEvents::REQUEST => ['onKernelRequest', 33],
];
}
public function onKernelRequest(RequestEvent $event)
{
$request = $event->getRequest();
if (!$request->isXmlHttpRequest()) {
$html = $this->twig->render('spa.html.twig', [
'uri' => $request->getUri(),
]);
$response = new Response($html, Response::HTTP_OK);
$event->setResponse($response);
}
}
}
In case of kernel.router use priority 1 at least.
You can use the php bin/console debug:event-dispatcher command to find out which listeners are registered for events and their priorities.

yii2 change database dynamically

I'm trying to move to another database dynamically. I've seen several questions that showed change db files from one to another and they just getting some information from next database. But what I need is completely moving to second database. How should I do this? I've seen that in order to achieve this dsn (in db.php file) should be altered. But I changed it and it's still not changed?? I should have full access to second database closing first one. Give me advice please
Configs like db.php are not intended to be changed in process (while PHP is processing). They are loaded once in the initialization, when request is entered the framework.
As an alternative, you can configure second DB beforehand in db.php, and change between them dynamically like:
Yii::$app->db // your default Database
and
Yii::$app->db2 // Second configured Database, to which you can switch dynamically later
You can learn about multiple database connections here
So, if you want ActiveRecord(for instance User) to be able to access two databases, you can define some static variable, which specifies from which DB to read/write. For example:
class User extends \yii\db\ActiveRecord
{
const DB_DATABASE1 = 'db1';
const DB_DATABASE2 = 'db2';
private static $db = self::DB_DATABASE1;
public static function setDb($db)
{
self::$db = $db;
}
public static function getDb()
{
switch (self::$db) {
case self::DB_DATABASE1:
return Yii::$app->db;
case self::DB_DATABASE2:
return Yii::$app->db2;
default:
throw new \Exception("Database is not selected");
}
}
//...
And then use it in Controller like:
User::setDb(User::DB_DATABASE1);
$usersDB1 = User::find()->all();
User::setDb(User::DB_DATABASE2);
$usersDB2 = User::find()->all();
Set up multiple database connections in your main.php or main-local.php configuration
Yii::$app->db1;
Yii::$app->db2;
Yii::$app->db3;
when making inquiries
$usersDB1=User::find()->all(Yii::$app->db1);
$usersDB2=User::find()->all(Yii::$app->db2);
$usersDB3=User::find()->all(Yii::$app->db3);
Globally switch to a different database dynamically
I have combined Yerke's answer above and Write & use a custom Component in Yii2.0 here.
First, create a component class to do the DB switching. Here, I call it DbSelector and put it in app\components\DbSelector.php.
namespace app\components;
use Yii;
use yii\base\Component;
use yii\base\InvalidConfigException;
class DbSelector extends Component {
const DB_MAIN = 'db';
const DB_SUB1 = 'db1';
const DB_SUB2 = 'db2';
private static $db = self::DB_MAIN;
public static function setDb($db)
{
self::$db = $db;
}
public static function getDb()
{
return \Yii::$app->get(self::$db);
}
}
Second, modify the config/web.php file or whichever your config file is to have multiple databases and add DbSelector as a Yii::$app component.
$config = [
'components' => [
//...
'db' => $db,
'db1' => $db1,
'db2' => $db2,
'dbSelector' => [
'class' => 'app\components\DbSelector',
],
//...
],
//...
];
Third, in each model class, add the following static function getDb() to call the DbSelector getDb():
public static function getDb()
{
return \Yii::$app->dbSelector->getDb();
}
For example,
class DiningTable extends \yii\db\ActiveRecord
{
public static function tableName()
{
return '{{%dining_table}}';
}
public static function getDb()
{
return \Yii::$app->dbSelector->getDb();
}
//...
}
class Customer extends \yii\db\ActiveRecord
{
public static function tableName()
{
return '{{%customer}}';
}
public static function getDb()
{
return \Yii::$app->dbSelector->getDb();
}
//...
}
Lastly, to globally switch to a different database, use \Yii::$app->dbSelector->setDb(YOUR_PREFERRED_DB),
use app\components\DbSelector;
//...
\Yii::$app->dbSelector->setDb(DbSelector::DB_SUB1);
$tables_1 = DiningTable::findAll();
$customers_1 = Customer::find()->where(['<', 'dob', '2000-01-01'])->all();
\Yii::$app->dbSelector->setDb(DbSelector::DB_MAIN);
$tables_main = DiningTable::findAll();
$customers_main = Customer::find()->where(['<', 'dob', '2000-01-01'])->all();

changing ZF2 form behavior when retrieving form

I'm wondering if there is a way to either pass additional parameters to the constructor (preferred) or retrieve the Request object to check the headers from within the Form constructor so that when I do a getForm in a controller, the form will be customized depending on how it is called?
I'm working on integrating AngularJs bindings and model tags into my form elements but I will need to modify how the submit button works whenever a form is called from Ajax vs being pulled into a Zend template via the framework.
Thus I would like to throw conditional parameters around where the submit button is added to the form, but I need to know if the rendered form is being viewed in zend or is being sent via an ajax call. I can detect the ajax call in the controller by looking at the request headers with isXmlHttpRequest(), but I'm not sure how to let the form know what the controller saw when it's retrieving the form with $this->getForm()
You can inject any options you like using a factory class.
use MyModule\Form\MyForm;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\FactoryInterface;
class MyFormFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $formElementManager)
{
$serviceManager = $formElementManager->getServiceLocator();
$request = $serviceManager->get('Request');
// I would recommend assigning the data
// from the request to the options array
$options = [
'is_ajax' => $request->isXmlHttpRequest(),
];
// Although you could also pass in the request instance into the form
return new MyForm('my_form', $options, $request);
}
}
If you inject the request you will need modify MyForm::__construct.
namespace MyModule\Form;
use Zend\Form\Form;
use Zend\Http\Request as HttpRequest;
class MyForm extends Form
{
protected $request;
public function __construct($name, $options, HttpRequest $request)
{
$this->request = $request;
parent::__construct($name, $options);
}
}
Update your module.config.php to use the factory
return [
'form_elements' => [
'factories' => [
'MyModule\Form\MyForm' => 'MyModule\Form\MyFormFactory'
]
]
]
Then ensure you request the form from the service manager (in a controller factory)
$myForm = $serviceManager->get('FormElementManager')->get('MyModule\Form\MyForm');
My AbstractForm with helper functions (I just added the getRequest() to the bottom). Of course in a wider scale application I'd probably add error checking to make sure these were not called from the constructor (when the service manager would not yet be available)
namespace Application\Form;
use Zend\Form\Form as ZendForm;
use Zend\Http\Request as HttpRequest;
use Zend\Stdlib\RequestInterface as Request;
use Zend\Form\FormElementManager as ZendFormElementManager;
use Zend\ServiceManager\ServiceLocatorAwareInterface as ZendServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface as ZendServiceLocatorInterface;
use Doctrine\ORM\EntityManager as DoctrineEntityManager
class AbstractForm extends ZendForm implements ZendServiceLocatorAwareInterface {
/**
* #var Request
*/
protected $request;
/**
* in form context this turns out to be Zend\Form\FormElementManager
*
* #var ZendFormElementManager $service_manager
*/
protected static $service_manager;
/**
* #var DoctrineEntityManager $entity_manager
*/
protected $entity_manager;
/**
* #var ZendServiceLocatorInterface $service_locator_interface
*/
protected $service_locator_interface;
public function __construct($name = null)
{
parent::__construct($name);
}
/**
* in form context this turns out to be Zend\Form\FormElementManager
*
* #param ZendFormElementManager $serviceLocator
*/
public function setServiceLocator(FormElementManager $serviceLocator)
{
self::$service_manager = $serviceLocator;
}
/**
* in form context this turns out to be Zend\Form\FormElementManager
*
* #return ZendFormElementManager
*/
public function getServiceLocator()
{
return self::$service_manager;
}
/**
* wrapper for getServiceLocator
* #return ZendFormElementManager
*/
protected function getFormElementManager() {
return $this->getServiceLocator();
}
/**
* this returns an actual service aware interface
*
* #return ZendServiceLocatorInterface
*/
protected function getServiceManager() {
if(!($this->service_locator_interface instanceof ZendServiceLocatorInterface)) {
$this->service_locator_interface = $this->getFormElementManager()->getServiceLocator();
}
return $this->service_locator_interface;
}
/**
* #return DoctrineEntityManager
*/
protected function getEntityManager() {
if(!($this->entity_manager instanceof \DoctrineEntityManager)) {
$this->entity_manager = $this->getServiceLocator()->getServiceLocator()->get('Doctrine\ORM\EntityManager');
}
return $this->entity_manager;
}
/**
* Get request object
*
* #return Request
*/
public function getRequest()
{
if (!$this->request) {
$this->request = $this->getServiceManager()->get('Request');
}
return $this->request;
}
}

CakePHP 3 - implementedEvents() - does not fire implemented event

Trying to start off with CakePHP's EventListener. I have set an event up, but it is not firing. I can't figure out why? This is the code I have so far...
public function view($slug = null) {
$profile = $this->Profiles->find()->where(['slug' => $slug])->first();
$this->set('profile', $profile);
$this->set('_serialize', ['profile']);
}
// I have confirmed this works.. but it is not calling the updateCountEvent method
public function implementedEvents(){
$_events = parent::implementedEvents();
$events['Controller.afterView'] = 'updateCountEvent';
return array_merge($_events, $events);
}
/**
* Triggered when a profile is viewed...
*/
public function updateCountEvent(){
Log::write('error', "Update count events"); // I dont get this line in the log. Not sure why this does not fire...
}
I revisited this question and was able to come up with a solution that works for me. Thanks Jose Lorenzo for the 'heads up'. This is my solution:
use Cake\Event\Event;
public function view($slug = null) {
$profile = $this->Profiles->find()->where(['slug' => $slug])->first();
$this->profileId = $profile->id;
$event = new Event('Controller.Profiles.afterView', $this);
$this->eventManager()->dispatch($event);
$this->set('title', $profile->name);
$this->set('profile', $profile);
$this->set('_serialize', ['profile']);
}
public function implementedEvents(){
$_events = parent::implementedEvents();
$events['Controller.Profiles.afterView'] = 'updateCountEvent';
return array_merge($_events, $events);
}
public function updateCountEvent(){
$profile = $this->Profiles->get($this->profileId);
$profile->set('views_count', $profile->views_count + 1);
$this->Profiles->save($profile);
}
I see the power of Events especially if I get to send out emails, update more tables and perhaps run a cron..., however instead of writing these 2 lines of code and create 2 more methods for this specific case, I could have made this simple call within the view action as such
$profile->set('views_count', $profile->views_count + 1);
$this->Profiles->save($profile);
The question would be.... Should I have opted for this simpler process, or stick with the events route?

Cakephp 2.0 change password

I am attempting to create a change password form in cakephp 2.0. I found a behavior that EuroMark created for 1.3 and now am having a tough time converting this code to work with 2.0. I know that it has something to do with the Auth Component as there were major changes to this component in 2.0.
public function validateCurrentPwd(Model $Model, $data) {
if (is_array($data)) {
$pwd = array_shift($data);
} else {
$pwd = $data;
}
$uid = null;
if ($Model->id) {
$uid = $Model->id;
} elseif (!empty($Model->data[$Model->alias]['id'])) {
$uid = $Model->data[$Model->alias]['id'];
} else {
return false;
}
if (class_exists('AuthExtComponent')) {
$this->Auth = new AuthExtComponent();
} elseif (class_exists($this->settings[$Model->alias]['auth'].'Component')) {
$auth = $this->settings[$Model->alias]['auth'].'Component';
$this->Auth = new $auth();
} else {
return true;
}
return $this->Auth->verifyUser($uid, $pwd);
}
I am getting an error on the line that reads $this->Auth = new $auth();
The error is as follows:
Argument 1 passed to Component::__construct() must be an instance of ComponentCollection, none given, called in C:\UniServer\www\new_company_test\app\Model\Behavior\change_password.php on line 117 and defined [CORE\Cake\Controller\Component.php, line 77]
and
Undefined variable: collection [CORE\Cake\Controller\Component.php, line 78]
it's also throwing this
Call to undefined method AuthComponent::verifyUser() in C:\UniServer\www\new_company_test\app\Model\Behavior\change_password.php on line 121
I am not sure if there is anything else that needs to be addressed in the script, I'm guessing not as there is no other place where Auth is used.
Any suggestions on what I need to do to get this to work? Any help is appreciated.
Thanks
you did discover that there is also a 2.0 branch, didnt you? :)
it should contain the same behavior:
https://github.com/dereuromark/tools/tree/2.0
either way, you need to pass a component collection into it:
$this->Auth = new AuthExtComponent(new ComponentCollection());
You should create a method verifyUser in your custom AuthExt Component which extends Auth Component for "current password" to work like so:
/**
* Quickfix
* TODO: improve - maybe use Authenticate
* #return bool $success
*/
public function verifyUser($id, $pwd) {
$options = array(
'conditions' => array('id'=>$id, 'password'=>$this->password($pwd)),
);
return $this->getModel()->find('first', $options);
$this->constructAuthenticate();
$this->request->data['User']['password'] = $pwd;
return $this->identify($this->request, $this->response);
}
/**
* returns the current User model
* #return object $User
*/
public function getModel() {
return ClassRegistry::init(CLASS_USER);
}
Maybe it is also possible to use the existing identify method in combination with a fake request object in the behavior directly?
I am thinking about using
$this->authenticate = array('Form'=>array('fields'=>array('username' => 'id')));
feel free to fork the behavior and submit a pull request.
"current password" is the only thing that is not yet cleanly solved right now.

Resources