PHPUnit in Laravel removes old database records when executing new test method - phpunit

I created a Laravel test class with PHPUnit which has two methods as below,
class MainLocationTest extends TestCase
{
use RefreshDatabase, TestUserTrait;
public static function getMainLocationCount(): int
{
return InventoryLocation::whereActive(1)->count();
}
/**
* Test main location creation
*/
public function testCreateMainLocation(): void
{
self::setActingAdminUser();
$this->assertEquals(0, self::getMainLocationCount());
$response = $this->postJson('/api/inventory/location/main/create', [
'location_name' => 'Test Main Location',
'location_address' => '',
'location_code' => '',
'location_manager_id' => '',
]);
$response->assertStatus(200);
$this->assertEquals(1, self::getMainLocationCount());
}
/**
* Test main location update
*/
public function testUpdateMainLocation(): void
{
self::setActingAdminUser();
$main_location = InventoryLocation::whereActive(1)->first();
$this->assertEquals('Test Main Location', $main_location->location_name);
$response = $this->postJson("/api/inventory/location/main/$main_location->id/edit", [
'location_name' => 'Test Main Location Updated',
'location_address' => '',
'location_code' => '',
'location_manager_id' => '',
]);
$response->assertStatus(200);
$main_location = InventoryLocation::find($main_location->id);
$this->assertEquals('Test Main Location Updated', $main_location->location_name);
}
}
My PHPUnit settings are,
<?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>
<testsuite name="Modules">
<directory suffix="Test.php">./Modules/*/Tests/Feature</directory>
<directory suffix="Test.php">./Modules/*/Tests/Unit</directory>
</testsuite>
</testsuites>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">./app</directory>
</include>
</coverage>
<php>
<env name="APP_ENV" value="testing"/>
<env name="BCRYPT_ROUNDS" value="4"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
<env name="MAIL_MAILER" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="TELESCOPE_ENABLED" value="false"/>
</php>
</phpunit>
When I run the test cases, testCreateMainLocation test get passed and testUpdateMainLocation test throw an error saying Attempt to read property "location_name" on null. But, when two methods are merged into one and run, all tests get passed.
It seems database is getting deleted when coming to testUpdateMainLocation test. So, I cannot get the location which was created in the testCreateMainLocation test. Is there a way to avoid database refresh till the end of execution of the class?
(I tried removing RefreshDatabase trait. But, it was not successfull)

Related

PHPUnit No such file or directory

I'm trying to test my RoomRepository with PHPUnit and Symfony 4. I installed symfony/phpunit-bridge using composer. I created a simple entity called Room with one id and name attributs and a repository method to get a Room by its id.
public function get(int $id): ?Room
{
/** #var Room $room */
$room = $this->findOneBy(['id' => $id]);
return $room;
}
My test is quite simple as you can see :
public function testGet(): void
{
/** #var RoomRepository $repository */
$repository = $this->em->getRepository(Room::class);
$room = $repository->get(1);
$this->assertCount(1, $room);
}
I am new with test and I don't know if it's the right way to proceed. I followed the Symfony documentation.
So, when I execute the following command :
./vendor/bin/simple-phpunit
I am getting this error :
Doctrine\DBAL\Exception\ConnectionException: An exception occurred in driver: SQLSTATE[HY000] [2002] No such file or directory
I am pretty sure this is a commun mistake and very easy to fix...
Furthermore, I wrote other simple asserts that worked very well. I don't think it's about PHPUnit configuration.
Here some informations about my env :
PHP 7.1
Symfony4.0.5
PHPUnit 5.7.27
Docker with Laradock (containers : mysql, apache2, workspace)
Thanks guys for reading my post and have a nice day :)
I had the same problem – I just forgot to add the database url to the phpunit.xml.dist file. You have to add:
<phpunit ...>
<php>
...
<env name="DATABASE_URL" value="mysql://username:password#server:port/database" />
...
</php>
...
</phpunit>
Of course with your own credentials instead of the placeholders.

How to remove /cache dir for Symfony Kernel after running tests?

When running test with Symfony Kernel, it creates /cache and /logs directory.
At the moment I load own bootstrap.php file with phpunit.xml:
<?php
require_once __DIR__.'/../vendor/autoload.php';
// clear cache
register_shutdown_function(function () {
Nette\Utils\FileSystem::delete(__DIR__.'/cache');
Nette\Utils\FileSystem::delete(__DIR__.'/logs');
});
I wonder, is there better way to do that?
The best without this extra bootstrap.php file?
Note: I don't want to keep /cache and /logs directory there by adding them to .gitignore.
Used resources with no help:
https://symfony.com/doc/current/testing.html
You could implement a test listener.
tests/ClearLogAndCacheTestListener.php
namespace Symplify\DefaultAutowire\Tests;
class ClearLogAndCacheTestListener extends \PHPUnit_Framework_BaseTestListener
{
public function endTestSuite(\PHPUnit_Framework_TestSuite $suite)
{
\Nette\Utils\FileSystem::delete(__DIR__.'/cache');
\Nette\Utils\FileSystem::delete(__DIR__.'/logs');
}
}
Then enable enable the test listener in your phpunit.xml config and remove the custom autoload.php from test folder:
phpunit.xml
<phpunit
bootstrap="vendor/autoload.php"
colors="true"
syntaxCheck="true"
verbose="true"
>
<listeners>
<listener class="Symplify\DefaultAutowire\Tests\ClearLogAndCacheTestListener">
</listener>
</listeners>
[...]
</phpunit>
Hope this help

Phpunit integration testing symfony app - how to inject services into test?

I want to test a class by checking the real database data after function is executed.
I do not understand, how can I inject services which I need, for example some repository class.
So far I have written this:
namespace Tests\integration\Service\JourneyRunner\EmailConditionCheck;
use PHPUnit_Framework_TestCase;
use NG\Model\Journey\EmailConditionCheckRepositoryInterface;
class EmailConditionCheckServiceTest extends PHPUnit_Framework_TestCase
{
private $emailConditionCheckQueueRepository;
public function __construct(
EmailConditionCheckQueueRepositoryInterface $emailConditionCheckQueueRepository
) {
$this->emailConditionCheckQueueRepository;
parent::__construct();
}
public function testPrepareEmailContentForSending()
{
echo 'aaa';
$this->assertEquals(1, 1);
}
}
I added the test service to services.xml
<parameter key="tests.integration.service.journey_runner.email_condition_check.email_condition_check_service_test.class">tests\integration\Service\JourneyRunner\EmailConditionCheck\EmailConditionCheckServiceTest</parameter>
<service id="tests.integration.service.journey_runner.email_condition_check.email_condition_check_service_test" class="%tests.integration.service.journey_runner.email_condition_check.email_condition_check_service_test%">
<argument type="service" id="ng.infrastructure.persistence.time_trigger_queue_repository" />
</service>
I know that argument is wrong currently, but from the error I see that it does not get even the wrong argument - it gets nothing.
PHP Fatal error: Uncaught TypeError: Argument 1 passed to Tests\integration\Service\JourneyRunner\EmailConditionCheck\EmailConditionCheckServiceTest::__construct() must be an instance of Tests\integration\Service\JourneyRunner\EmailConditionCheck\EmailConditionCheckQueueRepositoryInterface, none given, called in /var/www/api.notification.guru/ng-api-service/vendor/phpunit/phpunit/src/Framework/TestSuite.php on line 475 and defined in /var/www/api.notification.guru/ng-api-service/tests/integration/Service/JourneyRunner/EmailConditionCheck/EmailConditionCheckServiceTest.php:12
I tried to search for info, but I cannot find.
Found out how, not inject, but geting a container gives result I want:
<?php
namespace Tests\integration\Service\JourneyRunner\EmailConditionCheck;
use PHPUnit_Framework_TestCase;
use NG\Model\Journey\EmailConditionCheckRepositoryInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use NG\Model\Uuid;
class EmailConditionCheckServiceTest extends KernelTestCase
{
private $emailConditionCheckQueueRepository;
protected function setUp()
{
self::bootKernel();
$this->container = static::$kernel->getContainer();
$this->emailConditionCheckQueueRepository = $this->container->get('ng.infrastructure.persistence.email_condition_check_repository');
}
public function testPrepareEmailContentForSending()
{
$this->emailConditionCheckQueueRepository->get(Uuid::fromString('1'));
$this->assertEquals(1, 1);
}
}
Also needed to create phpunit.xml - set the app directory. App directory is the one where AppKernel.php lies in your project as I understood.
<?xml version="1.0" ?>
<phpunit>
<php>
<server name="KERNEL_DIR" value="app/" />
</php>
</phpunit>
And pass to phpunit command a parameter
--configuration=phpunit.xml

symfony functional tests webtestcase doesn't work

I try to write a functional test for my app but I am getting following error:
InvalidArgumentException: The option "test_case" must be set.
I tried googling for it but found literally no clues.
My code:
class WhateverTest extends WebTestCase {
public function testWhatever() {
$client = static::createClient();
$crawler = $client->request('GET', '/home');
$this->assertEquals(1, 1);
}
in phpunit.xml the kernel is set
<php>
<server name="KERNEL_DIR" value="app/" />
</php>
My AppKernel has just two functions: registerBundles() and registerContainerConfiguration
Check which WebTestCase class you are using. Most probably you are using
use Symfony\Bundle\FrameworkBundle\Tests\Functional\WebTestCase
and you should be using
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
So just change this one line and you should be good
you may have mistakenly used
Symfony\Bundle\SecurityBundle\Tests\Functional
or
Symfony\Bundle\FrameworkBundle\Tests\Functional\WebTestCase
instead of
Symfony\Bundle\FrameworkBundle\Test\WebTestCase,

Several troubles with PHPUnit

I have two controllers. First path of them is controller/news.php, second is controller/admin/news.php. I have routes for each controller.
For the controller/admin/news.php I have
Route::set('news-admin', 'admin/news(/)', array('start' => '\d+'))
->defaults(array(
'directory' => 'admin',
'controller' => 'news',
'action' => 'index',
'start' => 0,
));
For the controller/news.php:
Route::set('news', 'news(/)', array('start' => '\d+'))
->defaults(array(
'controller' => 'news',
'action' => 'index',
'start' => 0,
));
When I use a browser all work OK. When I call the
$response = Request::factory('/news')->execute()
route in an unittest , test runs. But when I call the
$response = Request::factory('admin/news')->execute()
I get only the next message
PHPUnit 3.7.8 by Sebastian Bergmann.
Configuration read from /home/mydir/Projects/www/kohsite/application/tests/phpunit.xml
After several experiments I understood that I can't test route contains a "directory" for controllers placed into subfolders.
Below I've shown my phpunit.xml
<phpunit bootstrap="bootstrap.php" colors="true">
<testsuite name="ApplicationTestSuite">
<directory>./classes</directory>
</testsuite>
<filter>
<whitelist>
<directory suffix=".php">../tests</directory>
<exclude>
<directory suffix="*">../cache</directory>
<directory suffix="*">../config</directory>
<directory suffix="*">../logs</directory>
<directory suffix=".php">../views</directory>
<file>../bootstrap.php</file>
</exclude>
</whitelist>
</filter>
</phpunit>
I assume you are using Kohana 3.x? Im not sure how you have your application setup but when I design a site that has admin controllers I usually create an admin controller that is not in a sub-folder. The default route can handle any requests to the http://domain.com/<controller>/<action>/<id > such as http://domain.com/admin/index.
If I wanted to have a controller specifically for the admin news I would create a folder named "admin", and setup the controller definition like this:
class Controller_Admin_News extends Controller_Admin {
I would then write a route in my bootstrap.php that looks like this:
Route::set('admin_news', 'admin/news(/<action>(/<id>))')
->defaults(array(
'controller' => 'admin_news',
'action' => 'index'
));
Try setting up your app like that and see if it helps.
Sure, I'm using Kohana 3.2. I have controller
class Controller_Admin_News extends Controller_Admin_Common

Resources