2016-04-12 29 views
6

Sto utilizzando bootable model trait per registrare determinati eventi per i modelli utilizzando il mio tratto. Tuttavia, ho riscontrato un problema nel tentativo di simulare modelli che utilizzano il tratto. In particolare, quando viene creata un'istanza di una versione di Mockery del modello, il suo codice di avvio concorda sul fatto che dovrebbe avere un metodo bootMyTrait, ma non può trovarlo quando tenta di chiamarlo.Problema di un tratto del modello avviabile di Laravel

Sample Repository per il seguito, con comandi da riprodurre.

A titolo di esempio, ecco un tratto:

namespace App; 
trait MyTrait 
{ 
    public static function bootMyTrait() 
    { 
     print("Booting MyTrait\n"); 
    } 
} 

e un modello di utilizzo:

namespace App; 
use Illuminate\Database\Eloquent\Model; 
class MyModel extends Model 
{ 
    use MyTrait; 
} 

Instantiating il modello funziona regolarmente bene. Questo mostra il risultato desiderato:

$model = new MyModel(); 

Tuttavia, cercando di deridere questo modello non coopera. Questo:

use Illuminate\Foundation\Testing\WithoutMiddleware; 
use Illuminate\Foundation\Testing\DatabaseMigrations; 
use Illuminate\Foundation\Testing\DatabaseTransactions; 


    class ExampleTest extends TestCase 
    { 
     /** 
     * A basic functional test example. 
     * 
     * @return void 
     */ 
     public function testTraitBooting() 
     { 
      $model = $this->getMock('App\MyModel'); 
     } 
    } 


Fails. Adding some debugging to Eloquent: 


    /** 
    * Boot all of the bootable traits on the model. 
    * 
    * @return void 
    */ 
    protected static function bootTraits() 
    { 
     $class = static::class; 

     foreach (class_uses_recursive($class) as $trait) { 
      print("\nTesting that class: $class has method: " . $method = 'boot'.class_basename($trait) . " because of Trait: $trait\n"); 
      if (method_exists($class, $method = 'boot'.class_basename($trait))) { 
       print("Class: $class has method: $method \n"); 
       try { 
        forward_static_call([$class, $method]); 
       } catch (\PHPUnit_Framework_MockObject_BadMethodCallException $e) { 
        print("Class: $class failed calling $method\n"); 
        throw $e; 
       } 
      } 
     } 
    } 

ci dà questo fallimento:

PHPUnit 5.1.0 by Sebastian Bergmann and contributors. 

E                 1/1 (100%) 
Testing that class: Mock_MyModel_9ee820db has method: bootMyTrait because of Trait: App\MyTrait 
Class: Mock_MyModel_9ee820db has method: bootMyTrait 
Class: Mock_MyModel_9ee820db failed calling bootMyTrait 


Time: 129 ms, Memory: 18.00Mb 

There was 1 error: 

1) ExampleTest::testTraitBooting 
PHPUnit_Framework_MockObject_BadMethodCallException: 

mock-bootable-laravel-model-trait/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:326 
mock-bootable-laravel-model-trait/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:309 
mock-bootable-laravel-model-trait/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:296 
mock-bootable-laravel-model-trait/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:277 
mock-bootable-laravel-model-trait/tests/ExampleTest.php:16 

ho provato anche creare modello diversi modi. Utilizzando DatabaseSoftDeletingTraitTest come esempio:

use Illuminate\Foundation\Testing\WithoutMiddleware; 
use Illuminate\Foundation\Testing\DatabaseMigrations; 
use Illuminate\Foundation\Testing\DatabaseTransactions; 
use Mockery as m; 

class ExampleTest extends TestCase 
{ 
    /** 
    * A basic functional test example. 
    * 
    * @return void 
    */ 
    public function testTraitBooting() 
    { 
     $mock = m::mock('App\MyModel'); 
     $mock->shouldReceive('bootMyTrait')->once(); 
    } 
} 

Ma qui, bootMyTrait non viene mai chiamato:

PHPUnit 5.1.0 by Sebastian Bergmann and contributors. 

E                 1/1 (100%) 

Time: 149 ms, Memory: 19.25Mb 

There was 1 error: 

1) ExampleTest::testTraitBooting 
Mockery\Exception\InvalidCountException: Method bootMyTrait() from Mockery_0_App_MyModel should be called 
exactly 1 times but called 0 times. 

mock-bootable-laravel-model-trait/vendor/mockery/mockery/library/Mockery/CountValidator/Exact.php:37 
mock-bootable-laravel-model-trait/vendor/mockery/mockery/library/Mockery/Expectation.php:271 
mock-bootable-laravel-model-trait/vendor/mockery/mockery/library/Mockery/ExpectationDirector.php:120 
mock-bootable-laravel-model-trait/vendor/mockery/mockery/library/Mockery/Container.php:297 
mock-bootable-laravel-model-trait/vendor/mockery/mockery/library/Mockery/Container.php:282 
mock-bootable-laravel-model-trait/vendor/mockery/mockery/library/Mockery.php:142 
mock-bootable-laravel-model-trait/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:122 

Quindi, posso spostare il codice che sto facendo nel metodo di avvio a un ServiceProvider, ma poi ho Avrai bisogno di registrare ogni modello che usa il tratto. Questo sembra sporco e l'uso del metodo di avvio sembra appropriato. Quindi penso di aver colto un bug o di aver simulato in modo errato il trait-using-model. Ho guardato allo getMockForTrait ma ho anche bisogno dell'istanza fittizia per estendere Eloquent (alcuni dei metodi del metodo chiamano metodi eloquenti)

Se qualcuno vede qualcosa che mi è sfuggito (o se mi sto avvicinando totalmente a questo nel modo sbagliato) , molto apprezzato

risposta

2

Dopo alcuni test, credo che tale metodo sarà sufficiente per testarlo:

$mock = m::mock('App\MyModel')->makePartial(); 
$mock->shouldReceive('bootMyTrait')->once(); 
$mock->__construct(); 

Spiegazione:

  1. $mock = m::mock('App\MyModel')->makePartial();

    Creiamo il mock ma lo rendiamo parziale perché vogliamo utilizzare il costruttore di classi predefinito e altri metodi.Rendendolo parziale vuol dire tutti i metodi che non hanno la precedenza verrà utilizzato dal originale classe App\MyModel

  2. $mock->shouldReceive('bootMyTrait')->once();

    Questo dovrebbe essere ovvio - vogliamo verificare se bootMyTrait metodo viene eseguito esattamente 1 ora

  3. $mock->__construct();

    In questo modo è possibile eseguire il costruttore di classi predefinito. Quando si crea una simulazione, sembra che non venga utilizzato alcun costruttore, quindi non possiamo testarlo nell'altro modo. Dobbiamo lanciare manualmente il metodo del costruttore di oggetti se vogliamo assicurarci che il costruttore di classi originale sia lanciato.

+0

Grazie! Non sono sicuro del motivo per cui questo non funziona con il wrapper Mockery di Laravel, ma lo prendo io! – timbroder