Testare i controller Laravel

Testare i controller non è la cosa più semplice del mondo. Bene, lasciatemi riformulare questo: testarli è un gioco da ragazzi; ciò che è difficile, almeno all'inizio, è determinante che cosa testare.

Un test del controller dovrebbe verificare il testo sulla pagina? Dovrebbe toccare il database? Dovrebbe assicurare che le variabili esistano nella vista? Se questo è il tuo primo passaggio sul fieno, queste cose possono confondere! Lasciami aiutare.

I test del controller devono verificare le risposte, assicurare che vengano attivati ​​i metodi di accesso al database corretti e affermare che le variabili di istanza appropriate vengono inviate alla vista.

Il processo di testare un controller può essere diviso in tre parti.

  • Isolato: Schernire tutte le dipendenze (forse escludendo il vista).
  • Chiamata: Attiva il metodo di controllo desiderato.
  • Garantire: Esegui affermazioni, verificando che lo stage sia stato impostato correttamente.

Il mondo Hello of Controller Testing

Il modo migliore per imparare queste cose è attraverso gli esempi. Ecco il "Ciao mondo"del test del controller in Laravel.

 client-> request ('GET', 'posts'); 

Laravel utilizza una manciata di componenti di Symfony per facilitare il processo di test di percorsi e viste, inclusi HttpKernel, DomCrawler e BrowserKit. Questo è il motivo per cui è fondamentale che i test PHPUnit ereditino da, no PHPUnit \ _Framework \ _TestCase, ma TestCase. Non preoccuparti, Laravel estende ancora il primo, ma aiuta a configurare l'app Laravel per i test, oltre a fornire una varietà di metodi di asserzione che sei incoraggiato a usare. Di più su questo a breve.

Nel frammento di codice sopra, facciamo un OTTENERE richiesta a / messaggi, o localhost: 8000 / messaggi. Supponendo che questa linea venga aggiunta ad una nuova installazione di Laravel, Symfony lancerà a NotFoundHttpException. Se stai lavorando, provalo eseguendo PHPUnit dalla riga di comando.

 $ phpunit 1) PostsControllerTest :: testIndex Symfony \ Component \ HttpKernel \ Exception \ NotFoundHttpException:

Nel umano-parlare, questo essenzialmente si traduce in "Ehi, ho provato a chiamare quella strada, ma non hai registrato nulla, pazzo!"

Come potete immaginare, questo tipo di richiesta è abbastanza comune al punto che ha senso fornire un metodo di supporto, come ad esempio $ This-> call (). In effetti, Laravel fa proprio questo! Ciò significa che l'esempio precedente può essere refactored, in questo modo:

 # app / tests / controller / PostsControllerTest.php public function testIndex () $ this-> call ('GET', 'posts'); 

Il sovraccarico è tuo amico

Anche se continueremo con le funzionalità di base in questo capitolo, nei miei progetti personali, faccio un passo avanti consentendo metodi come $ This-> get (), $ This-> post (), ecc. Grazie al sovraccarico di PHP, questo richiede solo l'aggiunta di un singolo metodo, che è possibile aggiungere a app / test / TestCase.php.

 # app / tests / TestCase.php funzione pubblica __call ($ method, $ args) if (in_array ($ method, ['get', 'post', 'put', 'patch', 'delete']))  restituire $ this-> call ($ method, $ args [0]);  lancia nuove BadMethodCallException; 

Ora sei libero di scrivere $ This-> get ( 'post') e ottenere lo stesso risultato dei precedenti due esempi. Come notato sopra, tuttavia, seguiamo le funzionalità di base del framework per motivi di semplicità.

Per superare il test, dobbiamo solo preparare il percorso corretto.

  

In esecuzione PHPUnit di nuovo ci restituirà al verde.


Le asserzioni di aiuto di Laravel

Un test che ti troverai a scrivere ripetutamente è quello che garantisce che un controller trasmetta una determinata variabile a una vista. Ad esempio, il indice metodo di PostsController dovrebbe passare a $ messaggi variabile alla sua vista associata, giusto? In questo modo, la vista può filtrare tutti i post e visualizzarli sulla pagina. Questo è un test importante da scrivere!

Se è un compito comune, allora, ancora una volta, non avrebbe senso che Laravel fornisse un'asserzione ausiliaria per realizzare proprio questa cosa? Certo che lo sarebbe. E, ovviamente, lo fa Laravel!

Illuminare \ Foundation \ Testing \ TestCase include una serie di metodi che ridurranno drasticamente la quantità di codice necessaria per eseguire asserzioni di base. Questo elenco include:

  • assertViewHas
  • assertResponseOk
  • assertRedirectedTo
  • assertRedirectedToRoute
  • assertRedirectedToAction
  • assertSessionHas
  • assertSessionHasErrors

I seguenti esempi di chiamate OTTIENI / post e verifica che le sue viste ricevano la variabile, $ messaggi.

 # app / tests / controller / PostsControllerTest.php public function testIndex () $ this-> call ('GET', 'posts'); $ This-> assertViewHas ( 'post'); 

Mancia: Quando si tratta di formattazione, preferisco fornire un'interruzione di riga tra l'asserzione di un test e il codice che prepara lo stage.

assertViewHas è semplicemente un po 'di zucchero che ispeziona l'oggetto risposta - da cui viene restituito $ This-> call () - e verifica che i dati associati alla vista contengano a messaggi variabile.

Quando ispeziona l'oggetto risposta, hai due scelte fondamentali.

  • $ Response-> getOriginalContent (): Recupera il contenuto originale o il reso vista. Facoltativamente, puoi accedere a originale proprietà direttamente, piuttosto che chiamare il getOriginalContent metodo.
  • $ Response-> getContent (): Recupera l'output renderizzato. Se una vista l'istanza viene restituita dal percorso, quindi getContent () sarà uguale all'output HTML. Questo può essere utile per le verifiche DOM, come "la vista deve contenere questa stringa."

Supponiamo che il messaggi percorso consiste di:

  

Dovremmo correre PHPUnit, emetterà un grido di aiuto passo successivo Messaggio:

 1) PostsControllerTest :: testIndex Failed che asserisce che un array ha la chiave "post".

Per renderlo verde, recuperiamo semplicemente i post e li passiamo alla vista.

 # app / routes.php Route :: get ('posts', function () $ posts = Post :: all (); restituisci View :: make ('posts.index', ['posts', $ posts]) ;);

Una cosa da tenere a mente è che, come il codice attualmente esiste, garantisce solo la variabile, $ messaggi, è passato alla vista. Non controlla il suo valore. Il assertViewHas opzionalmente accetta un secondo argomento per verificare il valore della variabile, nonché la sua esistenza.

 # app / tests / controller / PostsControllerTest.php public function testIndex () $ this-> call ('GET', 'posts'); $ this-> assertViewHas ('posts', 'foo'); 

Con questo codice modificato, unles la vista ha una variabile, $ messaggi, è uguale a foo, il test fallirà. In questa situazione, tuttavia, è probabile che preferiremmo non specificare un valore, ma dichiariamo invece che il valore sia un'istanza di Laravel Illuminare \ Database \ Eloquente \ Collection classe. Come potremmo riuscirci? PHPUnit fornisce un utile assertInstanceOf asserzione per colmare questo bisogno!

 # app / tests / controller / PostsControllerTest.php public function testIndex () $ response = $ this-> call ('GET', 'posts'); $ This-> assertViewHas ( 'post'); // getData () restituisce tutte le var collegate alla risposta. $ posts = $ response-> original-> getData () ['posts']; $ this-> assertInstanceOf ('Illuminate \ Database \ Eloquent \ Collection', $ posts); 

Con questa modifica, abbiamo dichiarato che il controller dovere passaggio $ messaggi - un'istanza di Illuminare \ Database \ Eloquente \ Collection - alla vista Eccellente.


Deridere il database

C'è un problema lampante con i nostri test finora. L'hai preso?

Per ogni test, una query SQL viene eseguita sul database. Sebbene questo sia utile per alcuni tipi di test (accettazione, integrazione), per i test di base del controller, servirà solo a ridurre le prestazioni.

L'ho perforato nel tuo cranio più volte a questo punto. Non siamo interessati a testare la capacità di Eloquent di recuperare i record da un database. Ha i suoi test. Taylor sa che funziona! Non sprechiamo tempo e potenza di elaborazione ripetendo gli stessi test.

Invece, è meglio prendere in giro il database e verificare semplicemente che i metodi appropriati siano chiamati con gli argomenti corretti. O, in altre parole, vogliamo assicurarcelo Messaggio :: tutti () non spara mai e colpisce il database. Sappiamo che funziona, quindi non richiede test.

Questa sezione dipenderà molto dalla libreria di Mockery. Per favore, ripassa quel capitolo del mio libro, se non lo conosci ancora.

Refactoring richiesto

Sfortunatamente, finora, abbiamo strutturato il codice in un modo che lo rende praticamente impossibile da testare.

 # app / routes.php Route :: get ('posts', function () // Ouch. Non possiamo testare questo !! $ posts = Post :: all (); return View :: make ('posts. indice ') -> con (' posts ', $ posts););

Questo è precisamente il motivo per cui è considerata una cattiva pratica quella di annidare chiamate Eloquent nei tuoi controllori. Non confondere le facciate di Laravel, che sono testabili e possono essere scambiate con mock (Coda :: shouldReceive ()), con i tuoi modelli Eloquent. La soluzione è iniettare il livello del database nel controller attraverso il costruttore. Questo richiede un po 'di refactoring.

Avvertimento: Memorizzare la logica all'interno di callback di route è utile per piccoli progetti e API, ma rendono i test incredibilmente difficili. Per applicazioni di qualsiasi dimensione considerevole, utilizzare i controller.

Registriamo una nuova risorsa sostituendo il messaggi percorso con:

 # app / routes.php Route :: resource ('posts', 'PostsController');

... e crea il controller di risorse necessario con Artisan.

 $ php artisan controller: make PostsController Controller creato con successo!

Ora, piuttosto che fare riferimento al Inviare modello direttamente, lo inietteremo nel costruttore del controller. Ecco un esempio sintetico che omette tutti i metodi restful tranne quello che siamo attualmente interessati a testare.

 post = $ post;  public function index () $ posts = $ this-> post-> all (); return View :: make ('posts.index') -> with ('posts', $ posts); 

Si noti che è una buona idea digitare typeint un'interfaccia, piuttosto che fare riferimento al modello Eloquent stesso. Ma, una cosa alla volta! Lavoriamo su a quello.

Questo è un modo significativamente migliore per strutturare il codice. Poiché il modello è stato iniettato, abbiamo la possibilità di sostituirlo con una versione derisoria per il test. Ecco un esempio di come fare proprio questo:

 mock = Mockery :: mock ('Eloquent', 'Post');  public function tearDown () Mockery :: close ();  public function testIndex () $ this-> mock -> shouldReceive ('all') -> once () -> andReturn ('pippo'); $ this-> app-> instance ('Post', $ this-> mock); $ this-> call ('GET', 'posts'); $ This-> assertViewHas ( 'post'); 

Il vantaggio di questa ristrutturazione è che, ora, il database non sarà mai inutilmente colpito. Invece, usando Mockery, verifichiamo semplicemente che il tutti il metodo viene attivato sul modello.

 $ this-> mock -> shouldReceive ('all') -> once ();

Sfortunatamente, se si sceglie di rinunciare alla codifica su un'interfaccia e invece di iniettare il Inviare modello nel controller, deve essere usato un po 'di inganno per aggirare l'uso della statica di Eloquent, che può scontrarsi con Mockery. Questo è il motivo per cui abbiamo il dirottamento di entrambi Inviare e Eloquente classi all'interno del costruttore del test, prima che le versioni ufficiali siano state caricate. In questo modo, abbiamo una lista pulita per dichiarare qualsiasi aspettativa. Il rovescio della medaglia, ovviamente, è che non possiamo default ad alcun metodo esistente, attraverso l'uso di metodi di Mockery, come makePartial ().

Il contenitore IoC

Il contenitore IoC di Laravel facilita drasticamente il processo di iniezione delle dipendenze nelle tue classi. Ogni volta che viene richiesto un controller, viene risolto dal contenitore IoC. In quanto tale, quando abbiamo bisogno di dichiarare che una versione finta di Inviare dovrebbe essere usato per il test, dobbiamo solo fornire a Laravel l'istanza di Inviare che dovrebbe essere usato.

 $ this-> app-> instance ('Post', $ this-> mock);

Pensa a questo codice come a dire "Ehi Laravel, quando hai bisogno di un'istanza di Inviare, Voglio che tu usi la mia versione derisoria."Perché l'app estende il Contenitore, abbiamo accesso a tutti i metodi IoC direttamente al di fuori di esso.

Dopo aver istanziato il controller, Laravel sfrutta la potenza del riflesso PHP per leggere il typehint e iniettare la dipendenza per te. Giusto; non è necessario scrivere una singola rilegatura per consentire ciò; è automatizzato!


reindirizzamenti

Un'altra aspettativa comune che ti troverai a scrivere è quella che garantisce che l'utente venga reindirizzato nella posizione corretta, magari dopo aver aggiunto un nuovo post al database. Come potremmo realizzare questo?

 # app / tests / controller / PostsControllerTest.php public function testStore () $ this-> mock -> shouldReceive ('create') -> once (); $ this-> app-> instance ('Post', $ this-> mock); $ this-> call ('POST', 'posts'); $ This-> assertRedirectedToRoute ( 'posts.index'); 

Supponendo che stiamo seguendo un sapore riposante, per aggiungere un nuovo post, lo faremmo INVIARE alla raccolta, o messaggi (non confondere il INVIARE metodo di richiesta con il nome della risorsa, che per caso ha lo stesso nome).

 $ this-> call ('POST', 'posts');

Quindi, abbiamo solo bisogno di sfruttare un'altra delle asserzioni di aiuto di Laravel, assertRedirectedToRoute.

Mancia: Quando una risorsa è registrata con Laravel (Percorso :: risorsa ()), il framework registrerà automaticamente i percorsi nominativi necessari. Correre percorsi php artigianali se dimentichi mai quali sono questi nomi.

Potresti preferire anche che il $ _POST superglobale è passato al creare metodo. Anche se non stiamo sottoponendo fisicamente un modulo, possiamo comunque consentirlo, tramite il Ingresso :: replace () metodo, che ci permette di "stub" questa matrice. Ecco il test modificato, che usa Mockery con() metodo per verificare gli argomenti passati al metodo a cui fa riferimento shouldReceive.

 # app / tests / controller / PostsControllerTest.php public function testStore () Input :: replace ($ input = ['title' => 'My Title']);

$ this-> mock -> shouldReceive ('create') -> once () -> con ($ input); $ this-> app-> instance ('Post', $ this-> mock); $ this-> call ('POST', 'posts'); $ This-> assertRedirectedToRoute ( 'posts.index');

percorsi

Una cosa che non abbiamo considerato in questo test è la convalida. Ci dovrebbero essere due percorsi separati attraverso il memorizzare metodo, a seconda che la validazione passi:

  1. Reindirizzare nuovamente al modulo "Crea messaggio" e visualizzare gli errori di convalida del modulo.
  2. Reindirizza alla raccolta o alla rotta indicata, posts.index.

Come best practice, ogni test dovrebbe rappresentare solo un percorso attraverso il codice.

Questo primo percorso sarà per la convalida non riuscita.

 # app / tests / controller / PostsControllerTest.php public function testStoreFails () // Imposta stage per una validazione fallita Input :: replace (['title' => "]); $ this-> app-> instance ('Posta ', $ this-> mock); $ this-> call (' POST ',' posts '); // La validazione fallita dovrebbe ricaricare il modulo di creazione $ this-> assertRedirectedToRoute (' posts.create '); // Gli errori deve essere inviato alla vista $ this-> assertSessionHasErrors (['title']);

Lo snippet di codice sopra dichiara esplicitamente quali errori dovrebbero esistere. In alternativa, puoi omettere l'argomento assertSessionHasErrors, in tal caso si limiterà a verificare che una busta messaggi sia stata visualizzata (nella traduzione, include il reindirizzamento withErrors ($ errori)).

Ora per il test che gestisce la convalida riuscita.

 # app / tests / controller / PostsControllerTest.php public function testStoreSuccess () // Imposta stage per validazione riuscita Input :: replace (['title' => 'Foo Title']);

$ this-> mock -> shouldReceive ('create') -> once (); $ this-> app-> instance ('Post', $ this-> mock); $ this-> call ('POST', 'posts'); // Dovrebbe reindirizzare alla raccolta, con un messaggio flash di successo $ this-> assertRedirectedToRoute ('posts.index', ['flash']);

Il codice di produzione per questi due test potrebbe essere simile a:

 # app / controller / PostsController.php public function store () $ input = Input :: all (); // Eseguiremo la convalida nel controller per comodità // Dovresti esportarlo nel modello, o un servizio $ v = Validator :: make ($ input, ['title' => 'required']); if ($ v-> fail ()) return Redirect :: route ('posts.create') -> withInput () -> withErrors ($ v-> messages ());  $ this-> post-> create ($ input); return Redirect :: route ('posts.index') -> with ('flash', 'Il tuo post è stato creato!'); 

Notare come il Validator è annidato direttamente nel controller? In generale, ti consiglio di riassegnare questo a un servizio. In questo modo, puoi testare la tua convalida separatamente da qualsiasi controller o percorso. Tuttavia, lasciamo le cose come sono per semplicità. Una cosa da tenere a mente è che non stiamo prendendo in giro il Validator, anche se certamente potresti farlo. Poiché questa classe è una facciata, può essere facilmente sostituita con una versione derisoria, tramite la facciata shouldReceive metodo, senza che ci si debba preoccupare di iniettare un'istanza tramite il costruttore. Vincere!

 # app / controller / PostsController.php Validator :: shouldReceive ('make') -> once () -> andReturn (Mockery :: mock (['fails' => 'true']));

Di volta in volta, scoprirai che un metodo che deve essere preso in giro dovrebbe restituire un oggetto stesso. Fortunatamente, con Mockery, questo è un gioco da ragazzi: dobbiamo solo creare una simulazione anonima e passare un array, che segnala rispettivamente il nome del metodo e il valore di risposta. Come tale:

 Mockery :: mock (['fails' => 'true']))

preparerà un oggetto, contenente a non riesce() metodo che restituisce vero.


repository

Per consentire una flessibilità ottimale, piuttosto che creare un collegamento diretto tra il controller e un ORM, come Eloquent, è meglio codificare su un'interfaccia. Il considerevole vantaggio di questo approccio è che, nel caso in cui doveste forse sostituire Eloquent per, ad esempio, Mongo o Redis, ciò richiede letteralmente la modifica di una singola riga. Ancora meglio, il controller non ha mai bisogno di essere toccato.

I repository rappresentano il livello di accesso ai dati della tua applicazione.

Quale potrebbe essere un'interfaccia per la gestione del livello del database di a Inviare Assomiglia a? Questo dovrebbe farti cominciare.

  

Questo può certamente essere esteso, ma abbiamo aggiunto i metodi minimi per la demo: tutti, trova, e creare. Si noti che le interfacce del repository vengono archiviate all'interno app / repository. Poiché questa cartella non è caricata automaticamente per impostazione predefinita, è necessario aggiornare il file composer.json file per l'applicazione per fare riferimento a esso.

 // composer.json "autoload": "classmap": [// ... "app / repositories"]

Quando una nuova classe viene aggiunta a questa directory, non dimenticare di compositore dump-autoload -o. Il -o, (ottimizzare) flag è facoltativo, ma dovrebbe sempre essere utilizzato come best practice.

Se tenti di iniettare questa interfaccia nel tuo controller, Laravel ti scatterà. Vai avanti; provalo e guarda Ecco la modifica PostController, che è stato aggiornato per iniettare un'interfaccia, piuttosto che il Inviare Modello eloquente.

 post = $ post;  public function index () $ posts = $ this-> post-> all (); return View :: make ('posts.index', ['posts' => $ posts]); 

Se si esegue il server e si visualizza l'output, si incontrerà la temuta (ma bellissima) pagina di errore Whoops, che dichiara che "PostRepositoryInterface non è istantaneo."


Se ci pensate, ovviamente il quadro è schiamazzante! Laravel è intelligente, ma non è un lettore di mente. È necessario sapere quale implementazione dell'interfaccia deve essere utilizzata all'interno del controller.

Per ora, aggiungiamo questo legame a app / routes.php. Più tardi, utilizzeremo invece i fornitori di servizi per archiviare questo tipo di logica.

 # app / routes.php App :: bind ('Repository \ PostRepositoryInterface', 'Repositories \ EloquentPostRepository');

Verbalizzare questa chiamata di funzione come "Laravel, piccola, quando hai bisogno di un'istanza di PostRepositoryInterface, Voglio che tu usi EloquentPostRepository."

app / repository / EloquentPostRepository sarà semplicemente un involucro attorno a Eloquent che implementa PostRepositoryInterface. In questo modo, non stiamo limitando l'API (e ogni altra implementazione) all'interpretazione di Eloquent; possiamo nominare i metodi come preferiamo.

  

Alcuni potrebbero sostenere che il Inviare il modello deve essere iniettato in questa implementazione per scopi di testabilità. Se sei d'accordo, semplicemente inseriscilo attraverso il costruttore, come al solito.

Questo è tutto ciò che dovrebbe prendere! Aggiorna il browser e le cose dovrebbero tornare alla normalità. Solo ora la tua applicazione è strutturata in modo migliore e il controller non è più collegato a Eloquent.

Immaginiamo che, tra qualche mese, il tuo capo ti informi che devi sostituire Eloquent con Redis. Bene, perché hai strutturato la tua applicazione in questo modo a prova di futuro, devi solo creare il nuovo app / repository / RedisPostRepository implementazione:

  

E aggiorna la rilegatura:

 # app / routes.php App :: bind ('Repository \ PostRepositoryInterface', 'Repositories \ RedisPostRepository');

Immediatamente, ora stai facendo leva su Redis nel tuo controller. Nota come app / controller / PostsController.php non è mai stato toccato? Questa è la sua bellezza!


Struttura

Finora in questa lezione, la nostra organizzazione è stata un po 'carente. Legature IoC in routes.php file? Tutti i repository raggruppati in una directory? Certo, potrebbe funzionare all'inizio, ma, molto rapidamente, diventerà evidente che questo non si ridimensiona.

Nella sezione finale di questo articolo, applicheremo PSR-ify al nostro codice e utilizzeremo i fornitori di servizi per registrare eventuali associazioni applicabili.

PSR-0 definisce i requisiti obbligatori che devono essere rispettati per l'interoperabilità del caricatore automatico.

Un caricatore PSR-0 può essere registrato con Composer, tramite PSR-0 oggetto.

 // composer.json "autoload": "psr-0": "Way": "app / lib /"

La sintassi può essere fonte di confusione all'inizio. Certamente era per me. Un modo semplice per decifrare "Way": "app / lib /" è pensare a te stesso "La cartella di base per Modo spazio dei nomi si trova in app / lib."Ovviamente, sostituisci il mio cognome con il nome del tuo progetto: la struttura della directory per abbinarla sarebbe:

  • app /
    • lib /
    • Modo/

Quindi, anziché raggruppare tutti i repository in a repository directory, un approccio più elegante potrebbe essere quello di categorizzarli in più directory, in questo modo:

  • app /
    • lib /
    • Modo/
      • Conservazione/
      • Inviare/
        • PostRepositoryInterface.php
        • EloquentPostRepository.php

È fondamentale aderire a questa convenzione sui nomi e sulle cartelle, se vogliamo che il caricamento automatico funzioni come previsto. L'unica cosa rimanente da fare è aggiornare i namespace per PostRepositoryInterface e EloquentPostRepository.

  

E per l'implementazione:

  

Eccoci; è molto più pulito Ma che dire di quei fastidiosi legami? Il file di rotte può essere un posto conveniente per sperimentare, ma non ha senso archiviarli in modo permanente. Invece, useremo i fornitori di servizi.

I provider di servizi non sono altro che classi di bootstrap che possono essere utilizzate per fare tutto ciò che si desidera: registrare un binding, collegarsi a un evento, importare un file di rotte, ecc..

Un fornitore di servizi Registrare() verrà attivato automaticamente da Laravel.

 app-> bind ('Way \ Storage \ Post \ PostRepositoryInterface', 'Way \ Storage \ Post \ EloquentPostRepository'); 

Per rendere questo file noto a Laravel, devi solo includerlo app / config / app.php, all'interno del fornitori schieramento.

 # app / config / app.php 'providers' => array ('Illuminate \ Foundation \ Providers \ ArtisanServiceProvider', 'Illuminate \ Auth \ AuthServiceProvider', // ... 'Way \ Storage \ StorageServiceProvider')

Buono; ora abbiamo un file dedicato per la registrazione di nuove associazioni.

Aggiornamento dei test

Con la nostra nuova struttura in atto, invece di prendere in giro il modello Eloquent, di per sé, possiamo invece prendere in giro PostRepositoryInterface. Ecco un esempio di uno di questi test:

 # app / tests / controller / PostsControllerTest.php public function testIndex () $ mock = Mockery :: mock ('Way \ Storage \ Post \ PostRepositoryInterface'); $ Mock> shouldReceive ( 'all') -> una volta (); $ this-> app-> instance ('Way \ Storage \ Post \ PostRepositoryInterface', $ mock); $ this-> call ('GET', 'posts'); $ This-> assertViewHas ( 'post'); 

Tuttavia, possiamo migliorare questo. È ovvio che ogni metodo all'interno PostsControllerTest richiederà una versione derisoria del repository. Pertanto, è meglio estrarre parte di questo lavoro di preparazione nel proprio metodo, in questo modo:

 # app / tests / controller / PostsControllerTest.php public function setUp () parent :: setUp (); $ This-> finto ( 'Way \ Storage \ Messaggio \ PostRepositoryInterface');  public function mock ($ class) $ mock = Mockery :: mock ($ class); $ this-> app-> instance ($ class, $ mock); ritorno $ mock;  public function testIndex () $ this-> mock-> shouldReceive ('all') -> once (); $ this-> call ('GET', 'posts'); $ This-> assertViewHas ( 'post'); 

Non male, ay?

Ora, se vuoi essere super-volatile, e sei disposto ad aggiungere un tocco di logica di prova al tuo codice di produzione, potresti addirittura eseguire il tuo derisione nel modello Eloquent! Ciò consentirebbe di:

 Messaggio :: shouldReceive ( 'all') -> una volta ();

Dietro le quinte, questo potrebbe prendere in giro PostRepositoryInterface, e aggiorna il binding IoC. Non puoi ottenere molto più leggibile di così!

Consentire questa sintassi richiede solo l'aggiornamento di Inviare modello, o, meglio, a BaseModel che tutti i modelli Eloquent si estendono. Ecco un esempio del primo: