2009-07-16 18 views
12

Ho un'applicazione Web PHP realizzata con framework CodeIgniter MVC. Vorrei testare varie classi di controller. Sto usando Toast per il test delle unità. I miei controller non hanno lo stato, tutto ciò che elaborano viene salvato in sessione o passato per essere visualizzato. Creare un oggetto di sessione di simulazione e testare se funziona correttamente è semplice (basta creare un oggetto fittizio e iniettarlo con $ controller-> session = $ mock).Come testare i controller con CodeIgniter?

Quello che non so è come lavorare con le viste. In CodeIgniter, viste vengono caricati come:

$this->load->view($view_name, $vars, $return); 

Dato che non voglio alterare il codice CI, io però ho potuto creare un finto Loader e sostituire l'originale. E qui sta il problema, non riesco a trovare un modo per ottenere una nuova classe da CI_Loader.

Se io non includono il/librerie file system/Loader.php, il CI_Loader di classe non è definita e non può ereditare da esso:

class Loader_mock extends CI_Loader 

Se faccio includere il file (utilizzando require_once), Ho ricevuto l'errore:

Cannot redeclare class CI_Loader 

Sembra che il codice CI stesso non usi require_once per qualsiasi motivo.

Qualcuno qui ha esperienza con le applicazioni di test dell'unità CodeIgniter?

Edit: ho cercato di iniettare un oggetto loader reale al run-time in una classe finto, e reindirizzare tutte le chiamate e variabili con __call, __set, __get, __isset e __unset. Ma non sembra funzionare (non ho alcun errore però, solo nessun output, cioè una pagina vuota di Toast). Ecco il codice:

class Loader_mock 
{ 
    public $real_loader; 
    public $varijable = array(); 

    public function Loader_mock($real) 
    { 
     $this->real_loader = $real; 
    } 

    public function __call($name, $arguments) 
    { 
     return $this->real_loader->$name($arguments); 
    } 

    public function __set($name, $value) 
    { 
     return $this->real_loader->$name = $value; 
    } 

    public function __isset($name) 
    { 
     return isset($this->real_loader->$name); 
    } 

    public function __unset($name) 
    { 
     unset($this->loader->$name); 
    } 

    public function __get($name) 
    { 
     return $this->real_loader->$name; 
    } 

    public function view($view, $vars = array(), $return = FALSE) 
    { 
     $varijable = $vars; 
    } 
} 

risposta

4

In alternativa, si potrebbe fare questo:

$CI =& get_instance(); 
$CI = load_class('Loader'); 

class MockLoader extends CI_Loader 
{ 
    function __construct() 
    { 
     parent::__construct(); 
    } 
} 

Poi nel controller fare $ this-> load = new MockLoader().

+0

Bella idea, grazie. –

2

La mia soluzione attuale è modificare il codice CodeIgniter per utilizzare require_once anziché require. Ecco la patch ho intenzione di inviare agli sviluppatori CI nel caso in cui qualcuno ha bisogno di fare la stessa cosa fino a che non accettano:

diff --git a/system/codeigniter/Common.php b/system/codeigniter/Common.php 
--- a/system/codeigniter/Common.php 
+++ b/system/codeigniter/Common.php 
@@ -100,20 +100,20 @@ function &load_class($class, $instantiate = TRUE) 
     // folder we'll load the native class from the system/libraries folder. 
     if (file_exists(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT)) 
     { 
-    require(BASEPATH.'libraries/'.$class.EXT); 
-    require(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT); 
+    require_once(BASEPATH.'libraries/'.$class.EXT); 
+    require_once(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT); 
       $is_subclass = TRUE; 
     } 
     else 
     { 
       if (file_exists(APPPATH.'libraries/'.$class.EXT)) 
       { 
-      require(APPPATH.'libraries/'.$class.EXT); 
+      require_once(APPPATH.'libraries/'.$class.EXT); 
         $is_subclass = FALSE; 
       } 
       else 
       { 
-      require(BASEPATH.'libraries/'.$class.EXT); 
+      require_once(BASEPATH.'libraries/'.$class.EXT); 
         $is_subclass = FALSE; 
       } 
     } 
1

non posso aiutare molto con i test, ma posso aiutare a prolungare la Libreria CI.

È possibile creare la propria classe MY_Loader all'interno di /application/libraries/MY_Loader.php.

<?php 
    class MY_Loader extends CI_Loader { 

    function view($view, $vars = array(), $return = FALSE) { 
     echo 'My custom code goes here'; 
    } 

    } 

CodeIgniter lo vedrà automaticamente. Inserisci semplicemente le funzioni che desideri sostituire nella libreria originale. Tutto il resto utilizzerà l'originale.

Per ulteriori informazioni, consultare CI manual page for creating core system classes.

+3

+1 per l'idea. Tuttavia, una regola d'oro del test delle unità è che non è necessario modificare il sistema solo per abilitare i ganci per i test delle unità. Idealmente, le classi che non fanno parte della suite di test unitari non dovrebbero avere alcun bagaglio di collaudo unitario con loro. Inoltre, mantenere tutto il codice di test unitario per una singola classe di test nello stesso file rende molto più facile da gestire (e anche rimuovere il codice di test unitario durante la distribuzione in produzione). –

1

Sono impressionato dal codice che si sta tentando di utilizzare.

Quindi ora mi chiedo come la classe 'Hooks' di CodeIgniter potrebbe essere di qualche aiuto per il tuo problema?

http://codeigniter.com/user_guide/general/hooks.html

Cordiali saluti, Rein Groot

+0

+1 per il suggerimento. Sembra che potrebbe essere fattibile, ma in qualche modo imbarazzante. Ho deciso di applicare la patch a CodeIgniter, è un semplice cambiamento e spero che un giorno entrerà nel codice CI tradizionale (non vedo perché non dovrebbe), quindi non dovrò correggerlo quando nuove versioni sono rilasciate. –

0

Il controller non deve contenere la logica del dominio, quindi i test di unità non hanno alcun senso.

Invece, testare i controller e le visualizzazioni con i test di accettazione.

+0

Bene ... mentre nei controller non esiste una logica di dominio, * sono * responsabili della gestione dell'input dell'utente. E alterando lo stato del livello del modello in base a quell'input. È lì che ci si concentrerà sui test unitari per i controllori. –

+0

Non penso che il controller debba alterare lo stato di qualsiasi cosa nel livello del modello. Dovrebbe solo ricevere la richiesta e quindi passarla al livello del modello (dopo aver gestito la sicurezza Web, ecc.). Se stai modificando entità o dati, suppongo che avresti bisogno di testare. Ma non dovresti farlo in primo luogo. – Patrick

+1

Se si passa il nome utente e la password al livello del modello, si sta modificando lo stato di esso. O se si invia un titolo dell'articolo da salvare, si modifica anche lo stato del livello del modello. E ciò che hai chiamato "sicurezza web" è qualcosa che dovrebbe essere eseguito nel livello del modello (forse eccetto la prevenzione CSRF ... che potrebbe essere meglio fare quando si inizializza l'istanza "Richiesta"). Tuttavia, è possibile che stiamo usando lo stesso termine per concetti [concetti] completamente diversi (http://stackoverflow.com/a/58640002727208). –

Problemi correlati