2012-02-17 11 views
11

Devo creare un oggetto fittizio con un insieme predefinito di proprietà in modo che possa essere utilizzato elseware nella base di codici dopo l'istanziazione.Creazione di un oggetto fittizio con proprietà predefinite

$mock = $this->getMock('MyClass', array(), array(), 'MyClass_Mock'); 
$mock->prop = 'foobar'; 

$myclassMock = new get_class($mock); 
var_dump($myclassMock->prop); // NULL 
// How can I make this dump 'foobar' ? 

sto testando parte del quadro che determina, individua e un'istanza di queste classi così iniettando l'oggetto deriso vanificherebbe lo scopo della prova.

Non ho bisogno di prendere in giro tutti i metodi .. solo creare dinamicamente una classe deriso in questo modo:

class MyClass_Mock extends MyClass { 
    public $prop = 'foobar'; 
} 

Edit: esempio semplificato

+0

Solo per chiarezza, vuoi prendere in giro una classe esistente e dare alle tue finte proprietà predefinite di quella classe, senza in realtà creare un'istanza della classe che viene derisa? – Leigh

+0

@Leigh Right, la classe verrà istanziata più in profondità nel framework. La classe che sto prendendo in giro è un modello astratto, e voglio dargli alcuni 'campi $ 'in modo che si comporti come un modello reale. So che PHPUnit crea mock scrivendo il codice in una stringa e evaling() esso. Non so come includere le dichiarazioni di proprietà in quel processo. –

risposta

4

Come ti senti sull'utilizzo di riflessione?

$r = new ReflectionClass('MyClass'); 

$props = $r->getDefaultProperties(); 

$mock = new stdClass; 

foreach ($props as $prop => $value) { 
    $mock->$prop = $value; 
} 

Non ho usato molto Reflection, solo per un'introspezione di base. Non sono sicuro se riuscirai a imitare completamente la visibilità, ecc. Usandolo, ma non vedo perché no se continui lungo il percorso di scrittura su una stringa e eval ing.

Edit:

Scanned attraverso le funzioni di riflessione per curiosità, è del tutto possibile per imitare completamente la classe con metodi fittizi, attuazione vincoli piena visibilità, costanti e gli elementi statici, se del caso, se si costruisce in modo dinamico la classe in una stringa e eval it.

Tuttavia sembra che sarà un completo missione per sostenere davvero completamente ogni possibilità, quando si tratta di ottenere i tipi di dati corretti (è necessario il codice per ricostruire un costruttore matrice da una matrice per esempio)

Buona fortuna se si va su questa strada, richiede più potenza del cervello di quanto io sono disposto a risparmiare in questo momento :)

Ecco un po 'di codice, è possibile fare la stessa cosa con le costanti, e creare metodi vuoti in modo simile.

class Test 
{ 
    private static $privates = 'priv'; 
    protected $protected = 'prot'; 
    public $public = 'pub'; 
} 

$r = new ReflectionClass('Test'); 

$props = $r->getDefaultProperties(); 

$mock = 'class MockTest {'; 

foreach ($props as $prop => $value) { 
    $rProp = $r->getProperty($prop); 


    if ($rProp->isPrivate()) { 
     $mock .= 'private '; 
    } 
    elseif ($rProp->isProtected()) { 
     $mock .= 'protected '; 
    } 
    elseif ($rProp->isPublic()) { 
     $mock .= 'public '; 
    } 

    if ($rProp->isStatic()) { 
     $mock .= 'static '; 
    } 

    $mock .= "$$prop = "; 

    switch (gettype($value)) { 
     case "boolean": 
     case "integer": 
     case "double": 
      $mock .= $value; 
      break; 
     case "string": 
      $mock .= "'$value'"; 
      break; 
/* 
"array" 
"object" 
"resource" 
*/ 
    case "NULL": 
      $mock .= 'null'; 
      break; 
    } 

    $mock .= ';'; 
} 

$mock .= '}'; 

eval($mock); 

var_dump(new MockTest); 
+2

Ti inviterò due volte se potessi. Grazie per l'impegno. Non ho ancora provato la rotta di riflessione ma sembra interessante. Darò alcuni dei tuoi suggerimenti e proverai come va :) –

3

io non sono sicuro che anche necessario di fare questo a scopo di test.

Generalmente, quando si esegue il test del codice che prevede l'accesso al modello, si utilizzano i dispositivi di fissaggio invece di prendere in giro i modelli reali perché i modelli sono strutture di dati "stupidi" che non espongono le funzionalità che devono essere prese in giro.

Il tuo esempio lo sopporta: se non è necessario simulare il comportamento (metodi), non è necessario un oggetto fittizio. È necessario un dispositivo di dati invece che il modello utilizzi come origine dati. Questo è particolarmente vero se, come dici tu, "l'iniezione di dipendenza non è un'opzione".

Ovviamente, se si decide di prendere in giro la modella in ogni caso, suggerirei la soluzione di riflessione di @ Leigh.

ho solo risposto a una domanda sui test di database di ieri che si potrebbe verificare per un po 'più in dettaglio: PHPUnit: How to test database interactions on remote Postgres server?

+0

Buoni punti. Immagino tu possa dire che sto provando a creare un dispositivo dinamico. Sto provando a testare la struttura del modello sottostante creando modelli finti. I nostri modelli sono configurati quasi interamente attraverso proprietà protette, quindi posso creare un modello utente semplicemente avendo 'protected $ _fields = array ('firstname,' lastname ');'. Il mio obiettivo è quello di avere la capacità di ricreare qualsiasi situazione costruendo un modello falso in modo dinamico per testare il framework sottostante. Ho già preso in giro i gateway dati e il resto del framework .. Ho solo bisogno della possibilità di creare classi dinamiche con oggetti di scena predefiniti –

+0

Aha vedo. In tal caso, con tutti i mezzi, prendi in giro quei modelli. Generalmente con qualcosa del genere avrei basato il modello su un oggetto "collezione" che memorizzava tutte le proprietà in un singolo array protetto e implementava 'ArrayAccess' con magic' __get' e '__set' per rendere semplice la lettura/scrittura delle proprietà. Se lo facessi, potresti lanciare il tuo metodo 'Model :: load' per popolare un modello in un colpo solo da un array chiave/valore. Un metodo 'load' come quello renderebbe popolare l'oggetto con tutte le informazioni che si desideravano piuttosto semplici. Inoltre, potrebbe essere esteso da classi figlio con convalida, ecc. Se necessario. – rdlowrey

1

Credo che il problema è che è necessario che il sistema sottoposto a test (framework) sia in grado di utilizzare new per creare un'istanza diretta degli oggetti del modello e ogni test deve impostare i valori predefiniti per le sue proprietà in modo diverso.

In tal caso, è possibile creare una classe base semplice per compilare un insieme predefinito di proprietà al momento della costruzione. La soluzione seguente utilizza late static binding da PHP 5.3, ma è possibile ottenere facilmente gli stessi risultati senza un leggero aggiustamento.

class MockModel 
{ 
    public static $properties; 

    public function __construct() { 
     if (isset(static::$properties) && is_array(static::$properties)) { 
      foreach (static::$properties as $key => $value) { 
       $this->$key = $value; 
      } 
     } 
    } 
} 

class MockBook extends MockModel { /* nothing needed */ } 

function testBookWithTitle() { 
    MockBook::$properties = array(
     'title' => 'To Kill a Mockingbird' 
    ); 
    $book = new MockBook; 
    self::assertEquals('To Kill a Mockingbird', $book->title); 
} 

Finché è possibile fornire il nome della classe da utilizzare con new al vostro quadro, questo dovrebbe funzionare. Se è necessario essere in grado di creare più di una istanza della stessa classe di simulazione durante una singola chiamata al framework, è necessario migliorare il precedente con una sorta di meccanismo di indicizzazione.

+0

È per il test del modello. Voglio creare un modello di autore e libri finto e testare la loro relazione. Quando uso '$ author-> books' il framework ha bisogno di istanziare un nuovo oggetto' Book' e leggere le sue proprietà per seminarlo con i dati e determinare i criteri di relazione da/per 'Author'. Quindi no, non posso davvero iniettare il mio oggetto 'Book' beffato da nessuna parte dato che è il lavoro di framework a istanziare dinamicamente questi oggetti. Ho solo bisogno di gettare una classe 'Author' in memoria con quelle proprietà senza definire concettualmente quella classe nel codice. –

+0

Guarda il mio ultimo esempio nella domanda. Devo solo essere in grado di scrivere quei tipi di classi in memoria senza doverli codificare in alcun modo. Pensavo di poter sfruttare il Mock Object Builder di PHPUnit ma non sono sicuro che abbia la funzionalità per farlo. –

+0

@MikeB - Guarda il mio aggiornamento. È questo ciò che stai cercando di ottenere? –

Problemi correlati