I am trying to do unit testing in laravel 7.x.
I created a testing database which is identic to the database I use for my website.
I modified the phpunit.xml file, I created a .env.testing file and I added a "testing" connection to the database.php file but I am getting an "SQLSTATE[42S02]: Base table or view not found: 1146 Table 'cbs_perform_test.cbs_defis' doesn't exist (SQL: select count(*) as aggregate from cbs_defis)" error.
I already checked multiple times the name of the database and the table to be sure I am using the correct one.
I already followed these guides : Use different database for testing and local ; How to Specify a Separate Database for Unit Testing on Laravel 5
Here is my phpunit.xml
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true">
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<directory suffix="Test.php">./tests/Feature</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./app</directory>
</whitelist>
</filter>
<php>
<server name="APP_ENV" value="testing"/>
<server name="BCRYPT_ROUNDS" value="4"/>
<server name="CACHE_DRIVER" value="array"/>
<server name="DB_CONNECTION" value="testing"/>
<server name="DB_DATABASE" value="cbs_perform_test"/>
<server name="MAIL_MAILER" value="array"/>
<server name="QUEUE_CONNECTION" value="sync"/>
<server name="SESSION_DRIVER" value="array"/>
<env name="DB_CONNECTION" value="testing"/>
</php>
My database.php connection
'testing' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'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' => '',
'prefix_indexes' => true,
'strict' => false,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
my .env.testing file
APP_ENV=testing
DB_CONNECTION=testing
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=cbs_perform_test
DB_USERNAME=root
DB_PASSWORD=
My test code
class DefiTest extends TestCase
{
use DatabaseMigrations;
use DatabaseTransactions;
/**
* A basic unit test example.
*
* #return void
*/
public function testCreateDefi()
{
$request = new Request();
$request->DEF_NOM = 'test';
$request->DEF_DESCRIPTION = 'testdescriptio ajhsg ln';
$request->DEF_NBSEMAINES = 2;
$request->DEF_CONSEILS = 'jhasnciu launh sl';
$request->DEF_VISIBLE = 1;
$request->DEF_DATE_VISIBLE = Carbon::now()->toDate();
$request->COA_ID = 3;
$dfc = new DefiCoachController();
$response = $dfc->createDefiTest($request);
$this->assertDatabaseHas('cbs_defis', $request->all());
}
}
The command I use for testing : php artisan test --env=testing
First of all, hope I can help you fix your problem as I am pretty sure it is a silly mistake you are making somewhere in the connection.
So, here are some tips:
Do not test your code "invoking" core framework code...
Instead of doing (unit testing):
$request = new Request();
$request->DEF_NOM = 'test';
$request->DEF_DESCRIPTION = 'testdescriptio ajhsg ln';
$request->DEF_NBSEMAINES = 2;
$request->DEF_CONSEILS = 'jhasnciu launh sl';
$request->DEF_VISIBLE = 1;
$request->DEF_DATE_VISIBLE = Carbon::now()->toDate();
$request->COA_ID = 3;
$dfc = new DefiCoachController();
$response = $dfc->createDefiTest($request);
$this->assertDatabaseHas('cbs_defis', $request->all());
Do (feature test):
$data = [
'nom' => 'test',
'description' => 'testdescriptio ajhsg ln',
'nbsemaines' => 2,
'conseils' => 'jhasnciu launh sl',
'visible' => 1,
'date_visible' => Carbon::now()->toDate(),
'coa_id' => 3,
];
$response = $this->post('your_desired_url_for_this_action', $data); // This can be get, post, put or delete
$this->assertDatabaseHas('cbs_defis', $data);
This way, you can make sure that:
Your URL is the one you want, without any typo or any errors
The controller is doing what it is supposed to do, inserting data in this case.
The controller is inserting the data you want it to insert. Let's say you have some processing behind curtains, here you can make sure that you sent "1 and 3" and it inserted "role X" (it is an example, let's say that would be your desired result after processing 1 and 3, so you are not directly inserting 1 and 3)
always avoid asserting data from where you are testing it. In your case, you are using Request object, let's say it is your custom class, and you do something when you do $request->attribute1 = 2, so when you read it back as $request->attribute1 maybe you have done some process to store it and you have modified it... if you are asserting that without explicitly saying assert that attribute1 is what I expect you are never asserting it. If you have a mistake in your code and instead of returning b (1 = a, 2 = b, etc.) the code will always pass, because you have stored it as something else than expected, but you are asserting for what it has done (let's say that your mistake returned c instead of b) so you are saying "find $request->attribute1 in the database" and you will have stored c instead of b (your expected value) and it will still find it and pass the test.
There is no need to create a new connection if it is the same except for DB_DATABASE or similar. In that case, you just define that info in .env.testing or in your phpunit.xml.
Also, no need to do <server name="DB_CONNECTION" value="testing"/> and <env name="DB_CONNECTION" value="testing"/>. If you see Laravel GitHub's phpunit.xml, you will see that they changed <env> to <server> on 5.7+, so stick to the one that corresponds to your version. There is a difference though that I cannot remember now, but for testing, there is no problem.
So, make sure you have set the right DB_HOST, DB_PORT, DB_USERNAME and DB_PASSWORD. You could have the same host but different port, or you could have same host and port but different database name, but same username and password. So make sure you are connecting to the correct database.
As your error is that it cannot find the desired table, clearly you are connecting to a database, so username and password should not be your problem, but the table does not exist.
One last important thing, are you using any trait on your tests ? There are some traits to automatically migrate the database and roll it back when finished, so there is no need for you to have your migrations sync manually in the testing environment. You should be using use RefreshDatabase; trait to do so.
Last tip, try to avoid doing DEF_SOMETHING because:
If your controller is related to Defi, there is no need to say "this is DEF data", we already know, so you can directly do something. Same for database, if table name is cars, avoid doing car_wheels, car_doors, etc., do wheels, doors, etc.
Avoid doing X_Y, prefer to do x_y, same for database. Stick to lowercase always and, for database, stick to snake_case, but for models' attributes, always stick to camelCase. (more info about cases)
Related
I have a query below that retrieves groups from the database; it works fine except when I run it through pest php test case and hit the controller; the test fails and says Illuminate\Database\QueryException: SQLSTATE[HY000]: General error: 1 no such function: YEAR
Here is my sql query
Group::query()
->selectRaw("YEAR(groups.created_at) as year,
MONTH(groups.created_at) as month,
DATE_FORMAT(groups.created_at, '%b') as short_month_format,
count('*') as count"
)
->whereIn('id', $ids)
->get();
Here is my pest php test case
test('authenticated user with permission will see analytics page ',function(){
$permission = 'permission';
actingWithPermission($this->user, $permission)
->get($this->route)
->assertStatus(302)
->assertDontSee("Text not to see goes here", false)
;
});
How can I fix mysql error above?
count('id') is wrong , use count('id') or other field .
I was able to resolve the above issue by changing DB CONNECTION in phpunit.xml from sqlite to mysql.
In my phpunit.xml
.........
<server name="APP_ENV" value="testing"/>
<server name="BCRYPT_ROUNDS" value="4"/>
<server name="CACHE_DRIVER" value="array"/>
<server name="DB_CONNECTION" value="mysql"/>
<server name="DB_DATABASE" value="database-name"/>
.........
I'm implementing a controller test case for our application that implemented with cakephp 2.x version.
I have 2 test case so far and planning to do it more. But, I'm stack in second test case and got fail message. If I comment out first test case and run it for second test case, I got the expected result. The only thing is if I have two test case, second test case is always fail. Here is the code snippet for my code.
My running command is ./cake test --stderr app Controller/EventsController and got error message is Undefined index: HTTP_HOST
I knew a bit weird situation that comment out first test case and never get this error and pass successfully for second test case. Appreciate your suggestion and ideas. Thank you so much.
App::uses('EventsController', 'Controller');
App::uses('UsersController', 'Controller');
class EventsControllerTest extends ControllerTestCase {
public function setUp(){
$this->testAction('/users/login/',[
'method' => 'POST',
'data' => [
'User' => [
'username'=> 'bk.ll#llmail.com',
'password'=> '123456',
'clientType'=>'llWeb'
]
]
]);
parent::setUp();
}
public function testEvents(){
$this->testAction('/admin/events/');
$this->assertInternalType('array', $this->vars['organisations']);
$this->assertInternalType('string', $this->vars['userRole']);
$this->assertInternalType('array', $this->vars['dutyRosterObj']);
$this->assertInternalType('string', $this->vars['date']);
$this->assertInternalType('boolean', $this->vars['isKkCdlc']);
}
public function testEventsList(){
$this->testAction('/admin/events/list/');
$this->assertInternalType('array', $this->vars['events']);
$this->assertInternalType('array', $this->vars['organisations']);
$this->assertInternalType('boolean', $this->vars['demoForSchool']);
$this->assertInternalType('integer', $this->vars['offset']);
}
}
It looks like You forget to config $_SERVER variables in phpunit.xml.dist file:
<php>
<server name="HTTP_HOST" value="example.org"/>
</php>
More info: https://phpunit.readthedocs.io/en/8.3/configuration.html
I am building the admission portal in laravel,
I have super admin database which has a schools table with 100 rows,
schools table structure
1.id
2.school_name
3.database details
I want to connect to the school database with its database details by its id.
technically
1.I will pass the school id from url
2.it will select that row from school table
3.after selecting the database details of particular school
4.will connect to the school database for further use.
I went through https://laracasts.com/discuss/channels/tips/set-up-dynamic-database-connection-globally
http://fideloper.com/laravel-multiple-database-connections
but no luck
please help to sort it out.
Actually you dont want multiple connections, but rather change existing connection.
public function setSchoolConnection($id) {
$school = School::find($id);
if ( $school ) {
config(['database.mysql' => [
'database' => $school->database,
'username' => $school->username,
'password' => $school->password
]]);
}
}
Now the default connection has been changed. I think.
If you don't want to change existing connection, just create a new connectio
config(['database.school' => [
'driver' => 'mysql',
'database' => $school->database,
'username' => $school->username,
'password' => $school->password
]]);
and use it like this
$users = DB::connection('school')->select(...);
Have you tried setting the connection in the School model? Laravel will take care of the rest, even if you have relations with different connection.
In your School model, I will override the constructor like so:
public function __construct(array $attributes = [])
{
$this->setConnection(env('DB_SCHOOL_CONNECTION'));
parent::__construct($attributes);
}
The one thing to be careful of is if you have a relation with a different connection, using whereHas or has in your query/builder wont work. as laravel will not be able to set the connection in the query generated.
I am new to Magento. I want to add a column in the newsletter_subscriber table so I made a new file mysql4-upgrade-1.6.0.0-1.6.0.1.php in app/code/core/mage/newsletter_setup/
<?php
$installer = $this;
$installer->startSetup();
$installer->getConnection()->addColumn(
$this->getTable('newsletter/subscriber'), //table name
'groupid', //column name
'varchar(100) NOT NULL' //datatype definition
);
$installer->endSetup();
?>
I updated the config file:
<modules>
<Mage_Newsletter>
<version>1.6.0.0</version>
</Mage_Newsletter>
</modules>
It doesn't work, please guide what I am doing wrong
It is not recommended to add/modify or do changes to any core files . Better you make a new module to add an extra column .
You have to mention correct version for module upgrade in app/code/local/your/module/sql/your_module_setup/upgrade-0.1.2-0.1.3.php file. (This means your upgrade the module version from 0.1.2 to 0.1.3). If your are not using upgrade script, remember to define <resources> in module config.xml and the setup script name is mysql4-install-0.1.0.php
Below is Mysql setup script file - upgrade-0.1.2-0.1.3.php
<?php
ini_set('display_errors', '1');
$installer = $this;
$installer->startSetup();
$installer->getConnection()
->addColumn(
$installer->getTable('newsletter/subscriber'), //Get the newsletter Table
'your_field_name', //New Field Name
array(
'type' => Varien_Db_Ddl_Table::TYPE_TEXT, //Field Type like TYPE_INTEGER ...
'nullable' => true,
'length' => 255,
'default' => 'Some thing default value',
'comment' => 'Your field comment'
)
);
$installer->endSetup();
?>
and after that change app/code/local/your/module/etc/config.xml version for example
<config>
<modules>
<NameSpace_ModuleName>
<version>0.1.3</version> <!-- if upgrade script version is 0.1.3 -->
</NameSpace_ModuleName>
</modules>
<global>
<resources>
<NameSpace_ModuleName_setup>
<setup>
<module>NameSpace_ModuleName</module>
<class>Mage_Catalog_Model_Resource_Setup</class>
</setup>
<connection>
<use>core_setup</use>
</connection>
</NameSpace_ModuleName_setup>
</resources>
</global>
</config>
With setup scripts they will be executed according to changes in the module's version.
In your case, your filename is mysql4-upgrade-1.6.0.0-1.6.0.1.php, while your version is 1.6.0.0. To get this particular script to execute you will need to bump the version to 1.6.0.1.
That being said - you're adding functionality to core Magento modules which is bad practice. You should instead move this into a local pool (app/code/local) module.
simply you can create a script and in you dbscripts folder and run this file from terminal or web browser.
e.g save file in pub/dbscripts/filename.php paste this code and change according to your requirement
<?php
use Magento\Framework\App\Bootstrap;
require '../../app/bootstrap.php';
$bootstrap = Bootstrap::create(BP, $_SERVER);
$obj = $bootstrap->getObjectManager();
$state = $obj->get('Magento\Framework\App\State');
$state->setAreaCode('frontend');
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
error_reporting(E_ALL);
ini_set('display_errors', 1);
$resource = $objectManager->get('Magento\Framework\App\ResourceConnection');
$connection = $resource->getConnection();
$salesTable = $resource->getTableName('Table_Name');
$sql = "ALTER TABLE ".$salesTable. " ADD `Field_name` varchar(255)";
$connection->query($sql);
echo"Script Run Successfully";
run this file from browser like
domain.name/pub/dbscripts/filename.php
I am fairly new to using laravel framework. I have the following requirement. I have a domain - example.com, and its entire code stack is running in laravel. Lets say in the config default database connection is - 'db1'
Now, if the url becomes - example.com/country - I want the default database connection to become - 'db2'
And also I want the base URL to be changed to example.com/country, since all the modules are going to be the same. Only the database connection is going to change here.
Could someone help me out here?
I would do it the following way:
Put the list of allowed countries into config file (countries.php) .
In routes.php:
// choosing country
$country = '';
if (in_array(Request::segment(1), Config::get('countries'))) {
$country = Request::segment(1);
}
// making route for top level
if ($country != '') {
Route::any( '/', 'MainPage#index');
}
// making routes optionally prefixed by country
Route::group(
array('prefix' => $country,
function () {
// here all routes
});
In database.php where you have defined your connection you could add another connections for example:
'germany' => array(
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'germany_connection',
'username' => 'root',
'password' => '',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
Now in the same file (although you should probably move it somewhere else) you can do:
if ($country == 'germany') {
DB::disconnect();
Config::set('database.default','germany');
DB::reconnect();
}
You can of course add many conditions here or if you have defined connection for each allowed country you can simply do:
if ($country != '' ) {
DB::disconnect();
Config::set('database.default', $country);
DB::reconnect();
}
It should work however I haven't tested it
I've made something similar using environments.
Add a new rule when detecting the environment to parse the URL and change it to the new environment. Then you can add a directory for the new environment where you set up the new database, base URL, etc.