2014-09-05 11 views
5

Scrivo felicemente test di unità, ma si scontrano quando li eseguo tutti insieme. Sto testando questa classe:PHPUnit: include classe dopo averlo deriso

class MyClass { 

    public function sayHello() { 
     return 'Hello world'; 
    } 
} 

utilizzando questo test. Tutti i test hanno una struttura come questa:

class MyClassTest extends PHPUnit_Framework_TestCase { 
    private $subject; 

    public static function setUpBeforeClass() { 
     require_once('path/to/MyClass.php'); 
    } 

    public function setUp() { 
     $this->subject = new MyClass(); 
    } 

    public function testSayHello() { 
     $this->assertEquals('Hello world', $this->subject->sayHello()); 
    } 
} 

MyClassTest corre benissimo da sè. Ma PHPUnit sarà incidente perché ho ridichiarare classe, se un altro test schernisce MyClass e capita di eseguire prima:

class Inject { 

    private $dependency; 

    public function __construct(MyClass $myClass) { 
     $this->dependency = $myClass; 
    } 

    public function printGreetings() { 
     return $this->dependency->sayHello(); 
    } 
} 

class InjectTest extends PHPUnit_Framework_TestCase { 

    public function testPrintGreetings() { 
     $myClassMock = $this 
      ->getMockBuilder('MyClass') 
      ->setMethods(array('sayHello')) 
      ->getMock(); 
     $myClassMock 
      ->expects($this->once()) 
      ->method('sayHello') 
      ->will($this->returnValue(TRUE)); 

     $subject = new Inject($myClassMock); 

     $this->assertEquals('Hello world', $subject->printGreetings()); 
    } 
} 

faccio usare un bootstrap.php di falsificare alcune funzioni globali non ancora refactoring.

Non ho caricatori automatici e non voglio elaborare-isolare OGNI test, perché ci vuole per sempre. Ho provato ad inserire combinazioni @runTestsInSeparateProcesses e/disabilitato nei docblocks di entrambi Test 1 & 2, ho ancora lo stesso errore.

+0

è necessario fornire un esempio riproducibile. – hek2mgl

+0

Perché stai configurando la tua simulazione tramite un'istruzione 'require_once' invece di usare la corretta funzionalità di simulazione usando l'API di Mock Builder? Leggi questo: https://phpunit.de/manual/current/en/test-doubles.html # test-doubles.mock-objects –

+1

@ hek2mgl, spero che questo esempio sia migliore. – PeerBr

risposta

5

Per comprendere questo comportamento, è necessario dare un'occhiata a come funziona PHPUnit. getMockBuilder()->getMock(), crea dinamicamente il seguente codice per la classe finta:

class Mock_MyClass_2568ab4c extends MyClass implements PHPUnit_Framework_MockObject_MockObject 
{ 
    private static $__phpunit_staticInvocationMocker; 
    private $__phpunit_invocationMocker; 

    public function __clone() 
    { 
     $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); 
    } 

    public function sayHello() 
    { 
     $arguments = array(); 
     $count  = func_num_args(); 

     if ($count > 0) { 
      $_arguments = func_get_ ... 

    # more lines follow ... 

Se MyClass non sia già stato caricato in questo momento, si aggiunge la seguente dichiarazione fittizia:

class MyClass 
{ 
} 

Questo codice sarà quindi ottenere analizzato usando eval() (!).

Dal PHPUnit eseguirà InjectTestprimaMyClassTest, ciò significa che il costruttore finto definirà la classe manichino e MyClass è già definito quando MyClassTest::setUpBeforeClass verrà chiamato. Ecco perché l'errore. Spero di poterti spiegare. Altrimenti, scavare nel codice di PHPUnit.


Soluzione:

cadere il metodo di setUpBeforeClass() e mettere la dichiarazione require_once in cima alle prove. setUpBeforeClass() non è pensato per includere le classi. Fare riferimento a docs.

Btw, avendo require_once sopra funziona perché PHPUnit includerà tutti i file di prova prima di di iniziare le prove.

test/MyClassTest.php

require_once __DIR__ . '/../src/MyClass.php'; 

class MyClassTest extends PHPUnit_Framework_TestCase { 
    private $subject; 

    public function setUp() { 
     $this->subject = new MyClass(); 
    } 

    public function testSayHello() { 
     $this->assertEquals('Hello world', $this->subject->sayHello()); 
    } 
} 

test/InjectTest.php

require_once __DIR__ . '/../src/Inject.php'; 

class InjectTest extends PHPUnit_Framework_TestCase { 

    public function testPrintGreetings() { 
     $myClassMock = $this 
      ->getMockBuilder('MyClass') 
      ->setMethods(array('sayHello')) 
      ->getMock(); 
     $myClassMock 
      ->expects($this->once()) 
      ->method('sayHello') 
      ->will($this->returnValue(TRUE)); 

     $subject = new Inject($myClassMock); 

     $this->assertEquals(TRUE, $subject->printGreetings()); 
    } 
} 
+0

Grazie. La tua spiegazione della dichiarazione "dummy' ha aiutato molto. Presumo che tutti i file di test php siano inclusi prima dell'esecuzione del primo test? – PeerBr

+0

@PeerBr Oh sì, tutti i file di test php sono inclusi prima dell'esecuzione del primo test, ecco perché la dichiarazione della classe dummy non si verificherà (poiché require_once in cima a MyClassTest è già stato eseguito). Questo è importante, mancato dire che ... – hek2mgl

Problemi correlati