How to test across multiple mysql schemas with PHPUnit? - php

I would like to test a process that queries across multiple schemas using PHPUnit's PHPUnit_Extensions_Database_TestCase class. I've dug through the documentation, SO and the source code, but it seems like I have to set up my database using something like:
protected function getConnection()
{
$this->pdo = new PDO('mysql:host=localhost;dbname=unit_test_schema', 'xxxx', 'yyyy');
return $this->createDefaultDBConnection($this->pdo, 'unit_test_schema');
}
protected function getDataSet()
{
return $this->createXMLDataSet(DB_SETUP_DIR.'/schema.xml');
}
Is there a way to somehow use more than one schema so I can test a query like:
SELECT *
FROM schema1.tableA
JOIN schema2.tableB
USING (id)
EDIT: To be clear, the issue I'm trying to resolve is that I can only figure out how to pass schema setup files to one database. I'd like to find a way to say "create tables1.xml in schema1 and tables2.xml in schema2." The tables referenced in the different xml files would be filled-and-killed for each test.

One thing I've done is created a definition for each schema, then overrode the definition when unit testing
define('schema1', 'schema1_prod');
define('schema2', 'schema2_prod');
Then for unit testing
define('schema1', 'unit_tests');
define('schema2', 'unit_tests');
If you have like table names across multiple schemas this will still break, but it should help if you don't.

Related

How can I mock the getPdo method of the DB facade in Laravel Unit Testing?

After taking on a project that is eager loading so much that it causes infinite loops in some effected areas, as a need for speed, some of the SQL was written through the getPdo method.
I now want to write Unit Tests for the associated changed areas and thus Mock the getPdo to point at the in memory SQLite. I am writing a short test to just see if I can mock the getPdo method succesfully by just asserting that I have a table my_table which has no results.
(new \PDO('sqlite::memory:'))->exec('CREATE TABLE my_table (id INTEGER PRIMARY KEY, name TEXT)');
$mock = Mockery::mock('alias:Illuminate\Support\Facades\DB');
$mock->shouldReceive('connection')
->andReturnSelf();
$mock->connection()->shouldReceive('getPdo')
->andReturnUsing(function () {
return new \PDO('sqlite::memory:');
});
DB::swap($mock);
$stmt = DB::connection()->getPdo()->prepare('select * from my_table');
$stmt->execute();
$this->assertEquals([], $stmt->fetchAll(\PDO::FETCH_OBJ));
Running this test is giving me the following error which I cannot seem to understand:
Static method Illuminate\Support\Facades\DB::swap() does not exist on this mock object
I am using Laravel 8.8X.X and usually, the DB::swap works perfectly fine when changing the underlying interface. How can I get around this?
Update:
Using the built in mocks as suggested, it doesn't find my table.
DB::shouldReceive('connection->getPdo')
->andReturnUsing(function () {
return new \PDO('sqlite::memory:');
});
SQLSTATE[HY000]: General error: 1 no such table: my_table
Update 2:
Mocking the Connection fixes the swap issue but I still cannot see my table during a SQL statement:
(new \PDO('sqlite::memory:'))->exec('CREATE TABLE my_table (id INTEGER PRIMARY KEY, name TEXT)');
$connection = Mockery::mock('Illuminate\Database\Connection');
$connection->shouldReceive('connection')->andReturnSelf();
$connection->connection()->shouldReceive('getPdo')
->andReturnUsing(fn() => new \PDO('sqlite::memory:'));
DB::swap($connection);
$stmt = DB::connection()->getPdo()->prepare('select * from my_table');
$stmt->execute();
$this->assertEquals([], $stmt->fetchAll(\PDO::FETCH_OBJ));
SQLSTATE[HY000]: General error: 1 no such table: my_table
Strangely, if I check if the mock is successful by doing:
$connection->connection()->shouldReceive('getPdo')->andReturnUsing(fn() => []);
$this->assertEquals([], DB::connection()->getPdo());
I get a passed test. Putting it back and doing more debug, The exec seems to be returning 0 when creating the table?
You are mocking the wrong class.
Laravel's facades are static wrappers around an instance of some class, in this case a database connection. In your test, you are replacing the entire static Facade with a mock:
$mock = Mockery::mock('alias:Illuminate\Support\Facades\DB');
Then later, you ask that mock Facade to swap out its underlying instance:
DB::swap($mock);
The mock class has no definition for "swap", so the mocking library complains.
What you wanted to do instead was mock the database connection itself, then tell the real Facade to use that as its instance. I don't have a Laravel project to hand to test, but I believe this is the right class:
$mockConnection = Mockery::mock('Illuminate\Database\Connection');
// Note that this is the real DB class, not a mock!
DB::swap($mockConnection);
There are also built-in helpers for mocking facades.

How to test custom DQL function?

I wrote a custom DQL function based on this tutorial.
I would like to write some tests, but the functionality of DQL is quite complex.
Is there any way how to test it?
As I see, even doctrine project not tests own DQL functions :(
I know it's an old quesion but for the folks who goggled it out as I did.
Testing is simple in fact. The general idea is to generate a query using your custom funciton then compare it against the expected result. Here is the example.
Let's say you have a custom DQL function which translates MY_FUNC(field) to MySQL_INTERNAL_FUNC(field). Then you test it as the following.
/* Somewhere in the PHPUnit test */
/* Assuming $em represents your EntityManager instance */
$query = $em->createQuery('
SELECT MY_FUNC(entity.someField)
FROM MyBundle:MyEntity entity
');
self::assertEquals('
SELECT MySQL_INTERNAL_FUNC(v0_.some_field) AS sclr_0
FROM my_entity v0_
', $query->getSQL());
That does the trick. Note the indents. They are there just to make the code clear. In practice Doctrine will remove them making SQL. So the expected SQL code should be in one line.

Cannot insert to my new table

I have created a migration for ratings, and the table also working when i am entering phpmyadmin.
The problem is, i cannot figure out, how to write to the table?
I am running the code from "story" controller
I am using this:
$z = new Rating();
$z->story_id = 10;
$z->save();
print_r($z);
My "ratings.php" model:
<?php
class Rating extends Eloquent {
protected $table = 'ratings';
}
?>
Is there some place where i should notify laravel that new Rating() means my table "ratings"?
It doesn't seem like i have done the migration correctly, but i am completely new still, so hope someone can figure it out for me.
well instead of using the save() function for laravel you can use the insert() function
Rating->insert_get_id(array('story_id' => '10'));
or
$insert_id = Rating->insert_get_id(array('story_id' => '10'));
for insertion into table.This is much easy to use and I have used this in my whole project and so far I haven't face any problems.
Also if you have not created the model for rating table then go to the models folder under application folder and create a file name rating.php and inside the file write this:
class Rating extends Eloquent
{
public static $timestamps = false;
}
Also please note that table which you created in the phpmyadmin should have name of the form "ratings".
I hope this can be of some help.
I don't really understand what you're doing. Are you trying to write into the table from php? Is Rating a sort of database connection class? You need to create a mysqli object to connect to the database, write a query, and get a result. For best security use a prepared statement. Mysqli Documentation Sorry if I'm off-base about your question, I'm just not positive about what it is.

Propel: how to remove link made via many-to-many relation

(link to previous question just in case: Struggling with one-to-many relation in an admin form)
I have this many-to-many relation in my Symfony-1.3 / Propel-1.4 project between User and Partner. When the User is being saved, if it has certain boolean flag being true, I want to clear all the links to the partners. Here is what I do at the moment and it doesn't work:
// inside the User model class
public function save(PropelPDO $con = null) {
if ($this->getIsBlaBla()) {
$this->setStringProperty(NULL);
$this->clearUserPartners();
}
parent::save($con);
}
Setting the string property to NULL works; looking at the DB clearly shows it. Thing is however, the USER_PARTNER table still holds the relations between the users and the partners. So I figured I have to clear the links one by one, like this:
foreach($this->getUserPartners() as $user_partner) {
$user_partner->delete();
//UserPartnerPeer::doDelete($user_partner); // tried that too
}
Both don't do the trick.
As I mentioned in my previous question, I am just monkey-learning Symfony via trial and error, so I evidently miss something very obvious. Please point me in the right direction!
EDIT: Here is how I made it work:
Moved the code to the Form class, like so:
public function doSave(PropelPDO $con = null) {
parent::doSave($con);
if ($this->getObject()->getIsSiteOwner()) {
$this->getObject()->setType(NULL);
$this->getObject()->save();
foreach($this->getObject()->getUserPartners() as $user_partner) {
$user_partner->delete();
}
}
return $this->getObject();
}
public function updateObject($values = null) {
$obj = parent::updateObject($values);
if ($obj->getIsSiteOwner()) {
$obj->clearUserPartners();
}
return $this->object;
}
What this does is:
When the boolean flag `is_site_owner` is up, it clear the `type` field and **saves** the object (ashamed I have not figured that out for so long).
Removes all existing UserPartner many-to-many link objects.
Clears newly associated (via the DoubleList) UserPartner relations.
Which is what I need. Thanks to all who participated.
Okey so now you have a many-to-many relation where in database terms is implemented into three tables (User , Parter and UserPartner). Same thing happens on Symfony and Propel, so you need to do something like this on the doSave method that should declare in UserForm:
public function doSave($con = null)
{
parent::doSave($con); //First all that's good and nice from propel
if ($this->getValue('please_errase_my_partners_field'))
{
foreach($this->getObject()->getUserPartners() as $user_partner_relation)
{
$user_partner_relation->delete();
}
}
return $this->getObject();
}
Check the method name "getUserPartners" that should be declared on the BaseUser.class.php (lib/model/om/BaseUser.class.php)
If you are learning Symfony, I suggest you use Doctrine instead of Propel because, I think Doctrine is simplier and more "beautiful" than Propel.
For your problem, I think you are on the good way. If I were you, I will keep my function save() I will write an other function in my model User
public function clearUserPartners(){
// You have to convert this query to Propel query (I'm sorry, but I don't know the right syntax)
"DELETE FROM `USER_PARTNER` WHERE user_id = '$this->id'"
}
With this function, you don't must use a PHP foreach.
But I don't understand what is the attribute StringProperty...
UserPartnerQuery::create()->filterByUser( $userObject )->delete();
or
UserPartnerQuery::create()->filterByUser( $partnerObject )->delete();
Had the same problem. This is a working solution.
The thing is that your second solution, ie. looping over the related objects and calling delete() on them should work. It's the documented way of doing things (see : http://www.symfony-project.org/book/1_0/08-Inside-the-Model-Layer#chapter_08_sub_saving_and_deleting_data).
But instead of bombing the DB with delete queries, you could just as well delete them in one go, by adding a method to your Peer class that performs the deletion using a simple DB query.

Zend Framework slow connect to Mysql [duplicate]

I am creating a web site using php, mysql and zend framework.
When I try to run any sql query, page generation jumps to around 0.5 seconds. That's too high. If i turn of sql, page generation is 0.001.
The amount of queries I run, doesn't really affect the page generation time (1-10 queries tested). Stays at 0.5 seconds
I can't figure out, what I am doing wrong.
I connect to sql in bootstrap:
protected function _initDatabase ()
{
try
{
$config = new Zend_Config_Ini( APPLICATION_PATH . '/configs/application.ini', APPLICATION_ENV );
$db = Zend_Db::factory( $config -> database);
Zend_DB_Table_Abstract::setDefaultAdapter( $db );
}
catch ( Zend_Db_Exception $e )
{
}
}
Then I have a simple model
class StandardAccessory extends Zend_DB_Table_Abstract
{
/**
* The default table name
*/
protected $_name = 'standard_accessory';
protected $_primary = 'model';
protected $_sequence = false;
}
And finally, inside my index controller, I just run the find method.
require_once APPLICATION_PATH . '/models/StandardAccessory.php';
$sa = new StandardAccessory( );
$stndacc = $sa->find( 'abc' );
All this takes ~0.5 seconds, which is way too long. Any suggestions?
Thanks!
Tips:
Cache the table metadata. By default, Zend_Db_Table tries to discover metadata about the table each time your table object is instantiated. Use a cache to reduce the number of times it has to do this. Or else hard-code it in your Table class (note: db tables are not models).
Use EXPLAIN to analyze MySQL's optimization plan. Is it using an index effectively?
mysql> EXPLAIN SELECT * FROM standard_accessory WHERE model = 'abc';
Use BENCHMARK() to measure the speed of the query, not using PHP. The subquery must return a single column, so be sure to return a non-indexed column so the query has to touch the data instead of just returning an index entry.
mysql> SELECT BENCHMARK(1000,
(SELECT nonindexed_column FROM standard_accessory WHERE model = 'abc'));
Note that Zend_Db_Adapter lazy-loads its db connection when you make the first query. So if there's any slowness in connecting to the MySQL server, it'll happen as you instantiate the Table object (when it queries metadata). Any reason this could take a long time? DNS lookups, perhaps?
The easiest way to debug this, is to profile your sql queries. you can use Firephp (plugin for firebug) see http://framework.zend.com/manual/en/zend.db.profiler.html#zend.db.profiler.profilers.firebug
another way to speed up things a little is to cache the metadata of your tables.
see: http://framework.zend.com/manual/en/zend.db.table.html#zend.db.table.metadata.caching
Along with the above suggestions I did a very unscientific test and found that the PDO adapter was faster for me in my application (I know mysqli is supposed to be faster but maybe it's the ZF abstraction). I show the results here (the times shown are only good for comparison)

Categories