2013-02-09 21 views
7

Puoi usare $ questo all'interno di callback per ottenere proprietà protette di classe mocked in phpunit? Oppure c'è un altro modo per riuscirci?

$mock = $this->getMock('A', array('foo')); 
$mock->expects($this->any())->method('foo')->will(
    $this->returnCallback(function() { 
     return $this->bar; 
})); 

Questo potrebbe essere davvero utile se pensi di iniettare oggetti derisi. A volte la classe ha una dipendenza hard-coded per l'altra classe, ma la crea con un metodo che potresti teoricamente prendere in giro e creare oggetto deriso invece di oggetto hardcoded. Per favore guarda l'altro esempio.

class A { 
    protected $bar = "bar"; 

    public function foo() { 
    $b = new B(); 
    return $b->fizz($this->bar); 
    } 
} 

class B { 
    public function fizz ($buzz) { 
    return $buzz; 
    } 
} 

Ma Diciamo classe B fa qualcosa di male e vorrei sostituirlo con finta.

$mockB = $this->getMock('B'); 
// (...) - and probably mock other things 
$mockA = $this->getMock('A', array('foo')); 
$mockA->expects($this->any())->method('foo')->will(
    $this->returnCallback(function() use ($mockB) { 
     return $mockB->fizz($this->bar); 
})); 

È in qualche modo realizzabile?

Naturalmente, senza alcuna sorpresa, attualmente, se faccio questo come sopra, allora ottengo l'errore:

PHP Fatal error: Using $this when not in object context in (...) 

Utilizzando use parola chiave che posso ereditare $ Mocka dal campo di applicazione genitore:

$mockB = $this->getMock('B'); 
// (...) - and probably mock other things 
$mockA = $this->getMock('A', array('foo')); 
$mockA->expects($this->any())->method('foo')->will(
    $this->returnCallback(function() use ($mockA, $mockB) { 
     return $mockB->fizz($mockA->bar); 
})); 

ma in questo modo cercherò di accedere alla barra come pubblico e otterrò:

PHP Fatal error: Cannot access protected property (...) 

risposta

0

S ince php 5.4 è possibile utilizzare $this nelle chiusure, ma è necessario restituire questo callback da oggetto, che contiene queste proprietà protette:

class A { 
    protected $bar = "bar"; 

    public function foo() { 
     $b = new B(); 
     return $b->fizz($this->bar); 
    } 

    public function getCallback(B $b) { 
     return function() use($b) { 
      return $b->fizz($this->bar); 
     }; 
    } 
} 

class B { 
    public function fizz ($buzz) { 
     return $buzz; 
    } 
} 

$mockA = new A; 
$mockB = new B; 

$callBack = $mockA->getCallback($mockB); 
var_dump($callBack() === $mockA->foo()); 

Tuttavia, se avete bisogno di ottenere il valore delle proprietà protette, è necessario definire getter pubblico per questo. In questo modo, il test funzionerà anche in php 5.3

+0

sì lo so che potrei definire getter pubblico, ma diciamo che è una libreria esterna o si desidera solo per tenerlo protetto. Tutta la questione riguarda come ottenere proprietà protette di classe in un determinato caso. Quindi per php 5.3 la risposta è ancora no, non puoi farlo. Per php 5.4, ottimo puoi usare $ this! È un enorme passo avanti! ma ho ancora bisogno di cambiare classe che non voglio cambiare. Potresti almeno creare una classe che estende A per aggiungere 'getCallback (B $ b)'? Hai bisogno di controllarlo. –

1

Come sottolineato da dev-null-dweller, in PHP 5.4 è possibile utilizzare $ this all'interno della chiusura come se si lavorasse normalmente nel metodo.

In 5.3 è possibile simulare questo comportamento facendo:

public function getCallback(B $b) { 
    $self = $this; 
    return function() use($b, $self) { 
     return $b->fizz($self->bar); 
    }; 
} 
7

Come altre risposte hanno sottolineato, $this può essere utilizzato in chiusure a partire da PHP 5.4. Un fatto meno noto è che puoi vincolare una chiusura a oggetti arbitrari e in effetti rendere le loro proprietà private accessibili in quel modo. Il metodo di cui hai bisogno è bindTo(), che restituisce una nuova chiusura con un contesto differente.

$cb = function() { 
    return $this->bar; 
}; 
$cb = $cb->bindTo($mockA); 

O, più precisa, il vostro esempio sarà simile a quanto segue:

$mockB = $this->getMock('B'); 
// (...) - and probably mock other things 
$mockA = $this->getMock('A', array('foo')); 
$fooCallback = function() use (&$mockB) { 
    return $mockB->fizz($this->bar); 
}; 
$mockA->expects($this->any())->method('foo')->will(
    $this->returnCallback($fooCallback->bindTo($mockA))); 
+1

+1 per aver menzionato questa funzione. PHPUnit incoraggia anche l'uso di PHP 5.4, quindi questo potrebbe essere un buon momento per iniziare a fare il passaggio. (nota anche a se stesso ... :)) – qrazi

Problemi correlati