2012-10-03 4 views
5

Sto inviando un'e-mail utilizzando la classe CakeEmail in un'azione di uno dei miei controller. Ho un test unitario per questo controller che funzionava bene prima di aggiungere il codice email. Dopo aver aggiunto l'e-mail ottengo questo errore:Utilizzare una configurazione email diversa durante il test dell'unità in CakePHP

SocketException: Could not send email.

Questo è giù per il fatto che io non ho alcun modo di inviare e-mail dal mio macchina locale.

Quindi ho pensato che sarebbe una buona idea avere due diverse opzioni di configurazione all'interno della classe EmailConfig in Config/email.php (Simile a come funziona il file di configurazione del database). L'impostazione predefinita che utilizza il trasporto di posta e un test che utilizza il trasporto di debug. Il problema è che, a differenza della configurazione del database, Cake non commuta automaticamente tra i due durante il test.

L'unica cosa che ho pensato è di aggiungere un costruttore alla classe EmailConfig e testare se stiamo testando l'unità, ma non sono sicuro di quale dovrebbe essere il controllo.

Qualcosa sulla falsariga di questo:

class EmailConfig { 

    public $default = array(
     'transport' => 'Mail' 
    ); 

    public $test = array(
     'transport' => 'Debug' 
    ); 

    public function __construct() { 
     if ($isUnitTesting) { 
      $this->default = $this->test; 
     } 
    } 

} 

Sarebbe il mio modo suggerito sopra essere una buona idea? In caso contrario, quali altri modi posso utilizzare un trasporto diverso per l'e-mail durante il test dell'unità?


Update - 4/10/2012

penso che stavo andando su questo nel modo sbagliato. Guardando a this answer sembra che anche la configurazione $default non sia caricata di default, devi specificarlo chiamando il metodo CakeEmail::config() o darglielo nel costruttore. Quindi penso che questo mi lasci due opzioni ora:

  • Nel controller controlla se siamo test di unità (in qualche modo?) E quindi utilizzare la configurazione 'test'.
  • Configurare il mio computer per poter inviare e-mail.
  • Preferisco fare il primo ma non so come questo possa essere fatto senza gonfiare l'azione del controller con i controlli se stiamo testando l'unità, sembra sbagliato farlo.

    +0

    Basta configurare la configurazione in "setUp" sul test case. Cordiali saluti, Cake viene fornito con un 'DebugTransport' che puoi usare nelle tue impostazioni di configurazione che faranno tutto tranne che effettivamente manderanno e restituiranno le intestazioni e il messaggio che dovrai usare nei tuoi test. Super utile :) – jeremyharris

    +0

    I tuoi problemi potrebbero essere più grandi del test. Le email falliscono regolarmente nella vita reale; assicurati che il codice conti per quello. Vorrei 1) assicurarmi che i test esistano sia per e-mail di successo che per quelle non riuscite e 2) provare a usare un oggetto simulato di CakeEmail. Non sei sicuro di come faresti il ​​# 2; altrimenti risponderei. –

    +0

    @jeremyharris Sapevo già di DebugTransport, infatti stavo guardando ai modi in cui poteva essere usato al posto di MailTransport. Sareste in grado di espandere come configurare la configurazione in setUp, non sono stato in grado di capire come potrebbe funzionare. – Josh

    risposta

    4

    Il modo più semplice è probabilmente passare a DebugTransport durante il test. Parte del test è che devi progettare il tuo programma affinché sia ​​testabile. In effetti, ci sono alcune funzioni qua e là in tutta Cake progettate per fare proprio questo. Per la vostra app, supponiamo che si invia una e-mail quando un utente si registra:

    App::uses('CakeEmail', 'Network/Email'); 
    App::uses('AppController', 'Controller'); 
    
    class UsersController extends AppController { 
    
        public function register() { 
        //registration logic 
        $email = new CakeEmail(); 
        $email->from(array('[email protected]' => 'Site')); 
        $email->to('[email protected]'); 
        $email->subject('Registered'); 
        $email->send('Thanks for registering!'); 
        } 
    
    } 
    

    Questo sembra innocuo, ma non si può deridere CakeEmail perché non consente dependency injection, che è necessaria durante il test. Invece, la classe CakeEmail dovrebbe essere istanziata in un modo che ci permetta di cambiarla in seguito. Per esempio:

    App::uses('CakeEmail', 'Network/Email'); 
    App::uses('AppController', 'Controller'); 
    
    class UsersController extends AppController { 
    
        public function register() { 
        //registration logic 
        $email = $this->_getEmailer(); 
        $email->from(array('[email protected]' => 'Site')); 
        $email->to('[email protected]'); 
        $email->subject('Registered'); 
        $email->send('Thanks for registering!'); 
        } 
    
        public function _getEmailer() { 
        return new CakeEmail(); 
        } 
    
    } 
    

    Perché abbiamo aggiunto un po 'di funzione di supporto, ora possiamo testarlo (deridendo la funzione di supporto).

    App::uses('CakeEmail', 'Network/Email'); 
    App::uses('UsersController', 'Controller'); 
    
    class UsersControllerTest extends ControllerTestCase { 
    
        public function testRegister() { 
        $controller = $this->generate('Users', array(
         'methods' => array(
         '_getEmailer' 
        ) 
        )); 
        $emailer = new CakeEmail(); 
        $emailer->transport('Debug'); 
        $controller 
         ->expects($this->any()) 
         ->method('_getEmailer') 
         ->will($this->returnValue($emailer)); 
        } 
    
    } 
    

    Questo test crea un oggetto fittizio per il nostro controller e gli dice di restituire il nostro recente creazione $emailer oggetto quando il metodo _getEmailer viene chiamato. Poiché $emailer ha impostato il trasporto su "Debug", è sicuro per i test.

    Naturalmente, dal momento che stiamo decidendo quale oggetto di posta elettronica viene restituito il metodo, il mocking dell'oggetto CakeEmail e l'attesa di determinati resi diventa banale.

    +0

    È possibile spostare la funzione _getEmailer su AppController per essere utilizzata in più controller e continuare a funzionare in questo modo? –

    +0

    @ BoštjanPišler Sì, a condizione che quei controller figlio non sovrascrivano '_getEmailer()' in un modo che cambi ciò che dovrebbe fare. (* Nota: questa è una risposta molto vecchia e si applica solo a Cake 2.x. *) – jeremyharris