laravel / ideas

Issues board used for Laravel internals discussions.
939 stars 28 forks source link

Support wrapping class tests in a database transaction #1350

Open Lenophie opened 6 years ago

Lenophie commented 6 years ago

Description:

To make full use of PHPUnit's test dependencies, I would like to be able to setup database transactions at class level instead of test level. A way I tried to implement this is to make use of PHPUnit's setUpBeforeClass and tearDownAfterClass but I meet the following error when trying to run the test :

vendor/bin/phpunit tests/Unit/TestTest.php 
PHPUnit 7.3.4 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)PHP Fatal error:  Uncaught Illuminate\Contracts\Container\BindingResolutionException: Target [Illuminate\Contracts\Debug\ExceptionHandler] is not instantiable. in /home/lenophie/dev/MoniCAG/vendor/laravel/framework/src/Illuminate/Container/Container.php:933
Stack trace:
#0 /home/lenophie/dev/MoniCAG/vendor/laravel/framework/src/Illuminate/Container/Container.php(773): Illuminate\Container\Container->notInstantiable('Illuminate\\Cont...')
#1 /home/lenophie/dev/MoniCAG/vendor/laravel/framework/src/Illuminate/Container/Container.php(646): Illuminate\Container\Container->build('Illuminate\\Cont...')
#2 /home/lenophie/dev/MoniCAG/vendor/laravel/framework/src/Illuminate/Container/Container.php(601): Illuminate\Container\Container->resolve('Illuminate\\Cont...', Array)
#3 /home/lenophie/dev/MoniCAG/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(733): Illuminate\Container\Container->make('Illuminate\\Cont...', Array)
#4 /home/lenophie/dev/MoniCAG/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/HandleEx in /home/lenophie/dev/MoniCAG/vendor/laravel/framework/src/Illuminate/Container/Container.php on line 933

In Container.php line 933:

  Uncaught Illuminate\Contracts\Container\BindingResolutionException: Target   
  [Illuminate\Contracts\Debug\ExceptionHandler] is not instantiable. in /home  
  /lenophie/dev/MoniCAG/vendor/laravel/framework/src/Illuminate/Container/Con  
  tainer.php:933                                                               
  Stack trace:                                                                 
  #0 /home/lenophie/dev/MoniCAG/vendor/laravel/framework/src/Illuminate/Conta  
  iner/Container.php(773): Illuminate\Container\Container->notInstantiable('I  
  lluminate\\Cont...')                                                         
  #1 /home/lenophie/dev/MoniCAG/vendor/laravel/framework/src/Illuminate/Conta  
  iner/Container.php(646): Illuminate\Container\Container->build('Illuminate\  
  \Cont...')                                                                   
  #2 /home/lenophie/dev/MoniCAG/vendor/laravel/framework/src/Illuminate/Conta  
  iner/Container.php(601): Illuminate\Container\Container->resolve('Illuminat  
  e\\Cont...', Array)                                                          
  #3 /home/lenophie/dev/MoniCAG/vendor/laravel/framework/src/Illuminate/Found  
  ation/Application.php(733): Illuminate\Container\Container->make('Illuminat  
  e\\Cont...', Array)                                                          
  #4 /home/lenophie/dev/MoniCAG/vendor/laravel/framework/src/Illuminate/Found  
  ation/Bootstrap/HandleEx                                                     

PHP Fatal error:  Uncaught Illuminate\Contracts\Container\BindingResolutionException: Target [Illuminate\Contracts\Debug\ExceptionHandler] is not instantiable. in /home/lenophie/dev/MoniCAG/vendor/laravel/framework/src/Illuminate/Container/Container.php:933
Stack trace:
#0 /home/lenophie/dev/MoniCAG/vendor/laravel/framework/src/Illuminate/Container/Container.php(773): Illuminate\Container\Container->notInstantiable('Illuminate\\Cont...')
#1 /home/lenophie/dev/MoniCAG/vendor/laravel/framework/src/Illuminate/Container/Container.php(646): Illuminate\Container\Container->build('Illuminate\\Cont...')
#2 /home/lenophie/dev/MoniCAG/vendor/laravel/framework/src/Illuminate/Container/Container.php(601): Illuminate\Container\Container->resolve('Illuminate\\Cont...', Array)
#3 /home/lenophie/dev/MoniCAG/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(733): Illuminate\Container\Container->make('Illuminate\\Cont...', Array)
#4 /home/lenophie/dev/MoniCAG/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/HandleEx in /home/lenophie/dev/MoniCAG/vendor/laravel/framework/src/Illuminate/Container/Container.php on line 933

I marked this issue as a feature request because I couldn't find any documentation on the subject whether it be in the docs or online, hinting that this may be an oversight rather than a bug.

Steps To Reproduce:

Here is the test prompting the error :

<?php

namespace Tests\Unit;

use Illuminate\Support\Facades\DB;
use Tests\TestCase;

class TestTest extends TestCase
{
    public static function setUpBeforeClass() {
        Parent::createApplication();
        Parent::setUpBeforeClass();
        DB::beginTransaction();
    }

    public function testExample()
    {
        $this->assertTrue(true);
    }

    public static function tearDownAfterClass()
    {
        DB::rollBack();
        Parent::tearDownAfterClass();
    }
}

Note that the use of Parent::createApplication(); is only to prevent another error from appearing, an error which I don't really understand either.

Thanks for reviewing this issue,

therealchiko commented 6 years ago

Hi @Lenophie

This seems like a challenge I addressed recently by simply having my TestCase as follows:

<?php

namespace Tests;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

abstract class TestCase extends BaseTestCase
{
    use CreatesApplication, RefreshDatabase;
}

You should get a fresh DB instance after each class is run.

Lenophie commented 6 years ago

Hello, Thanks for your answer, I already tried similar solutions but I think they are sub-optimal compared to transactions :

Class-level transactions would be much cleaner and performant.

deleugpn commented 6 years ago

use DatabaseTransactions;

Lenophie commented 6 years ago

@deleugpn As I stated in the original post, use DatabaseTransactions; is inappropriate because I want to be able to use PHPUnit's tests dependencies. As the transactions are rolled back between each test, the dependant tests surely fail.