PHPunit: Problems with testing - php

I have run into a major problem while writing tests. I am using Laravel 5.6.0 as framework and PHPUnit 7.0 for testing. As a testing DB I have used sqlite with storage in memory. This is from my database.php:
'sqlite_testing' => [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => '',
],
But my problem is that I have several places where I use whereRaw, for example $query->whereRaw('STR_TO_DATE(CONCAT(date, " ",from), "%Y-%m-%d %k") < ?', [$before]);. The problem here is that sqlite does not have the STR_TO_DATE or CONCAT functions that MySQL has. So PHPUnut throws a bunch of errors because of that.
My solution was instead using a MySQL DB as testing DB. But this doesn't seem to work since I get several different errors, mostly I have several tests where foreign key constraint fails.
One exaple for this is that I have the following in my Base TestCase setUp method:
if (Schema::hasTable('gym_schedule') && !empty(GymSchedule::createGymSchedules())) {
$this->artisan('db:seed', ['--class' => 'GymScheduleTableSeeder']);
}
This fails every time except the first because it says that a schedùle with id 1 already exists (id is my primary key). I did try to truncate all tables between each test class using tearDown, but that did not help at all, and also the testing became reeeeally slow, like 3 seconds for each test.
So basically that approach does not work either.
I am at a loss. I have tried googling this and searching through StackOverflow. I am open to any suggestion that is not too complicated (either remove all MySQL functions somehow, solve usage of MySQL or anything else really).
Does anyone have a good idea?

Regarding the first part of the question, unit testing a Laravel app when using whereRaw(), this is how I wrote the methods:
$raw_condition = "CONCAT(`country_code`, `number`) = ?";
if (env('DB_CONNECTION') === 'sqlite') {
$raw_condition = "(`country_code` || `number`) = ?";
}
return Model::whereRaw($raw_condition, [$number])->firstOrFail();

Related

What is the best way to troubleshoot this error with parent::find?

I'm trying to build a PHP RESTful service using PhalconPHP. I'm new to PHP, so this may be a beginner question. I am following a tutorial https://www.toptal.com/phalcon/phalcon-php-restful-apis, and in the tutorial there is a section where it makes a call to pull the list of users.
public static function find($parameters = null)
{
return parent::find($parameters);
}
I am passing in a list of parameters listed below.
Array
(
[conditions] =>
[bind] => Array()
[columns] => users_id
)
but I keep getting a 500 error. I'm fairly sure it has to do with how I am connecting (or in this case, NOT connecting to the database).
I'm not 100% sure on what the 'parent' part does either, nor how it connects up to my MySQL database - I think I've gotten the config setup, and I've passed in the tablename that I'm expecting it to be under, but I have no idea if it's actually connecting up and then failing, or if the call itself is failing. So far I've tried echo and print_r with as many variables as I can find, but so far I'm not getting any information on why this is failing.
What is a good way to go about troubleshooting this? How can I find out what the 'parent' is? How can I find out if I'm connecting to my database even, or if it's failing before then?
In your concrete example, the quotes are missing in your conditions and columns parameters, it should be:
$users = Users::find(
[
'conditions' => '',
'bind' => [],
'columns' => "users_id"
]
);
Which should return a list of users ids (if any).
That tutorial from Andrew Belousoff is very good but maybe the next step after Phalcon's REST tutorial, since it explains step by step the inner workings of Phalcon.
For debugging, you can also check Phalcon's guide about it: https://docs.phalconphp.com/en/3.4/debug
And after Belousoff, you can dive into deeper waters with this one: https://github.com/phalcon/phalcon-api
Error 500 means error in php, just check logs. Im not sure how this parent thing is related to phalcon. This is just OOP, you mean you are using framework without knowledge about php/oop? Parent is just parent class which you extends.

Laravel Unit testing controllers error when using multiple methods

I am trying to test some of my controllers through Unit Testing. But there is something strange happening. With the following code in my testcase:
public function test_username_registration_too_short()
{
$result = $this->action('POST', 'App\\Controllers\\API\\UserController#store', null, [
'username' => 'foo'
]);
$this->assertEquals('not_saved', $result->getContent());
// $result = $this->action('POST', 'App\\Controllers\\API\\UserController#store', null, [
// 'username' => 'foo'
// ]);
// $this->assertEquals('not_saved', $result->getContent());
}
public function test_username_registration_too_short_run_2()
{
$result = $this->action('POST', 'App\\Controllers\\API\\UserController#store', null, [
'username' => 'foo'
]);
$this->assertEquals('not_saved', $result->getContent());
}
When I run this, the initial too_short test passes, but the exact same code on run 2 does not pass (it even manages to save the user). But if I put that same code twice in the same method (what is commented out now) it works perfectly? I have nothing in my setUp or tearDown methods. And I am a bit lost here.
The code in the controller is the following:
$user = new User(Input::all());
if($user->save())
{
return 'saved';
}
return 'not_saved';
I'm not going to stop repeating myself over this question. There's a similar answer to a (somewhat) similar question. TL;DR: don't use unit testing framework for functional / integration testing.
This is area of functional testing and there is a fabulous framework
called Behat. You should do your own research, but essentially, while
PHPUnit is great at testing more or less independent blocks of
functionality it sucks at testing bigger things like full request
execution. Later you will start experiencing issues with session
errors, misconfigured environment, etc., all because each request is
supposed to be executed in it's own separate space and you force it
into doing the opposite. Behat on the other hand works in a very
different way, where for each scenario (post robot, view non-existing
page), it sends a fresh request to the server and checks the result.
It is mostly used for final testing of everything working together by
making assertions on the final result (response object / html / json).
If you want to test your code the proper way consider using the right tools for that. Once you know your way around with Behat you'll fall in love with it + you can use PHPUnit from within the Behat, to make individual assertions.

How to insert a sample data with PHPSpec and Laravel

I change my environment to testing on basespec with sqlite as driver and store in the memory.
function it_should_validate()
{
// Create the user
$data = array('email' => 'test#gmail.com', 'password' => 'password', 'remember' => false);
$this->validate($data)->shouldBe(true);
}
How do i insert information to the testing database whenever i run the test? Right now the test fails because it always return false;
What you're trying to do is integration testing.
PhpSpec is a tool for unit testing. It puts a big emphasis on testing in isolation. That means, that the only object that's created is the object being tested. All its dependencies should be stubbed or mocked.
In other words there should be no database involved in specs you write.
You didn't show the full example of a class you're trying to spec, so it's hard to advice how your spec should look like.
Adviced reading:
Test Driven Development by Example
Growing Object-Oriented Software Guided by Tests

Database connection issue with long time running shell in CakePHP

I have following database configuration in database.php file from my CakePHP app:
public $default = array(
'datasource' => 'Database/Mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'root',
'password' => '',
'database' => 'database',
'prefix' => '',
);
All is working fine, except of one queue shell script. The script is looping and waiting for commands to run (for example to update some reports). After a while 1-2 days database data is changing, but the script will still "see" the old data, and results of command is wrong. If I restart shell script, the results are OK... for few days.
I have to mention that I had "lost database connection" issue before in the script and I have solved it by runing every 10-15 min:
$user = $this->User->find('first');
Now I am affraid this is making the connection persistent somehow...
How can I reset the database connection ?
EDIT:
I was just refactoring the code to check if I can set $cacheQueries to false on the Model. But in few parts of the code I am using ConnectionManager directly, and only then I have "cache" problem. If I query database from Model->find results are ok. I need direct queries for performance reasons in few places...
$query = "SELECT COUNT(1) as result
FROM
......
";
$db = ConnectionManager::getDataSource('default');
$result = $db->query($query);
The property $cacheQueries which #burzum mentioned, seems not to be in use in any cake model method.
But I found another interesting fact in the source of the DboSource.
You need to use the second parameter of the DboSource::query() method to turn off the caching. Or the third if you want to provide additional parameters for the DboSource::fetchAll() method.
Eventhough this will fix your problem, you should write your queries with the Model::find() method that CakePHP offers.
You should only not use them if they are seriously impacting your performance.
func0der
Try to set these two model properties to false:
$cacheQuery http://api.cakephp.org/2.4/source-class-Model.html#265
$cacheSources http://api.cakephp.org/2.4/source-class-Model.html#499

How to get rid of MySQL error 'Prepared statement needs to be re-prepared'

I've rewritten my site php-code and added MySQL Stored Procedures.
In my local version everything works fine but after I uploaded my site to hosting server I'm constantly getting fatal error 'Prepared statement needs to be re-prepared'.
Sometimes page loads, sometimes loading fails and I see this error. What's that?
This is a possibility: MySQL bug #42041
They suggest upping the value of table_definition_cache.
You can read about statement caching in the MySQL docs.
#docwhat's answer seems nice, but on a shared hosting server, not everyone is allowed to touch the table_open_cache or table_definition_cache options.
Since this error is related to prepared statements, I have tried to 'emulate' those with PDO by providing the following option:
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass, [
PDO::ATTR_EMULATE_PREPARES => true
]);
Note: actually this is in a Laravel 5.6 project, and I added the option in config/database.php:
'connections' => [
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => true,
'engine' => null,
'options' => [
PDO::ATTR_EMULATE_PREPARES => true,
],
],
(...)
],
I have not tested the impact of emulating prepared statements on the duration of loading my site, but it works against the error SQLSTATE[HY000]: General error: 1615 Prepared statement needs to be re-prepared I got.
Update on the performance: the emulated version seems to be slightly faster (32.7±1.4ms emulated, 35.0±2.3ms normal, n=10, p-value=0.027 for two-tailed Student's T-test).
In short: Don't use VIEWS in prepared statements.
This seems to be an on-going issue
Views are messy to handle with Dynamic SQL
Earliest Bug was Cannot create VIEWs in prepared statements from 11 years ago. There was a patch put in to address it.
Another bug report, Prepared-Statement fails when MySQL-Server under load, states that error 1615 is not a bug when the underlying tables are busy. (Really ?)
While there is some merit to increasing the table cache size (See MySql error when working with a mysql view), it does not always work (See General error: 1615 Prepared statement needs to be re-prepared (selecting mysql view))
ALTERNATIVES
Over a year ago, someone mentioned this in the MySQL Forum (MySql “view”, “prepared statement” and “Prepared statement needs to be re-prepared”).
Someone came up with the simple idea of not using the view in the prepared statement but using the SQL of view in a subquery instead. Another idea would be to create the SQL used by the view and execute it in your client code.
These would seems to be better workarounds that just bumping up the table cache size.
First gain access to mysql shell:
mysql
Check the value of the table_definition_cache:
show global variables like '%table_definition_cache%';
It might be 400 or 1400.
Enlarge it:
set global table_definition_cache = 4000;
Good to go!
Issue: 'Prepared statement needs to be re-prepared'
This issue generally occurs at the time of calling procedure either by using any Computer Language(like Java) or Calling Procedures from the backend.
Solution: Increase the size of the cache by using (executing) below script.
Script: set global table_definition_cache = 4000;
just do this:
SET GLOBAL table_definition_cache = 4096;
SET GLOBAL table_open_cache = 4096;
4096 can be to less, so set it to a higher value. But make sure that both values ​​are the same.
FLUSH TABLES; comand on database solved for me, i was using doctrine orm.
my solutions is to create a routine like this:
DELIMITER $$
--
-- Procedimientos
--
DROP PROCEDURE IF EXISTS `dch_content_class_content`$$
CREATE DEFINER=`renuecod`#`localhost` PROCEDURE `dch_content_class_content`(IN $classId INTEGER)
BEGIN
-- vw_content_class_contents is a VIEW (UNIONS)
select * from vw_content_class_contents;
END$$
I hope this help someone
This is a workaround for people who are on shared hosting and don't want to risk it with sql injection. According to this post:
Laravel Fluent Query Builder Join with subquery
you could store the definition of your view in a function
private function myView(){
return DB::raw('(**definition of the view**) my_view_name)');
}
and then use it like this:
public function scopeMyTable(Builder $query)
{
return $query->join($this->myView(),'my_view_name.id','=','some_table.id');
}
This is a laravel approach, but I'm sure it could be applied in most cases and doesn't need huge code refactoring or architecture change. Plus, it's relatively secure as your statements stay prepared
I had this error being caused by a large group_concat_max_len statement
SET SESSION group_concat_max_len = 1000000000000000000;
removed it and the error went away
Okay, we were stuck on a laptop that would not allow Tableau to update or retrieve data from views on our MariaDB 10.3.31 databases. We did the usual, google and stack overflow, lots of solutions that just would not work. However, we have another laptop that did connect and ran just fine. So after many head scratches, a eureka moment came.
The offending laptop had both V5.3.14 and V8.0.26 ODBC connectors installed. We removed the V8.0.26 ODBC connector and Bang, all the view issues disappeared.
Hope someone finds this solution useful.
I tried to run an UPDATE query which was joining view and a table, and also got the same error. Since I had no user input, I decided to run DB::unprepared which fixed the problem.
You can read more about it in the Laravel Documentation.
I was getting this same error in Ruby on Rails in a shared hosting environment. It may not be the most secure solution, but disabling prepared statements got rid of the error message for me.
This can be done by adding the "prepared_statements: false" setting to your database.yml file:
production:
prepared_statements: false
This seems like a reasonable solution when you don't have control over the configuration settings on the MySQL server.

Categories