2016-08-31 30 views
14

Mamy kilka starszych projektów laravel, które wykorzystują fasady na zajęciach.Testy integracyjne kpinające fasady kontra wstrzykiwanie

use Cache; 

LegacyClass 
{ 
    public function cacheFunctionOne() 
    { 
     $result = Cache::someFunction('parameter'); 

     // logic to manipulate result 

     return $result; 
    } 

    public function cacheFunctionTwo() 
    { 
     $result = Cache::someFunction('parameter'); 

     // different logic to manipulate result 

     return $result; 
    } 
} 

Nasze nowsze projekty używać Dependency Injection z podstawowych klas laravel że fasady reprezentują jak został hinted at by Taylor Otwell himself. (Używamy konstruktora wtrysku dla każdej klasy, ale aby utrzymać przykład krótkie, tutaj używam zastrzyk metodę i używać jedną klasę.)

use Illuminate\Cache\Repository as Cache; 

ModernClass 
{ 
    public function cacheFunctionOne(Cache $cache) 
    { 
     $result = $cache->someFunction('parameter'); 

     // logic to manipulate result 

     return $result; 
    } 

    public function cacheFunctionTwo(Cache $cache) 
    { 
     $result = $cache->someFunction('parameter'); 

     // different logic to manipulate result 

     return $result; 
    } 
} 

wiem elewacje can be mocked

public function testExample() 
{ 
    Cache::shouldReceive('get') 
       ->once() 
       ->with('key') 
       ->andReturn('value'); 

    $this->visit('/users')->see('value'); 
} 

który działa dobrze dla testy jednostkowe. Problemem, który próbuję zrozumieć, jest to, że te fasady są wyśmiewane "globalnie".

Na przykład wyobraźmy sobie, że piszę test integracji (testowanie kilku połączonych klas podczas kpiny z usług - a nie końcowy test z wykorzystaniem usług na żywo), który w pewnym momencie wykonuje dwie oddzielne klasy , które zawierają tę samą fasadę który wywołuje tę samą metodę z tymi samymi parametrami:.

W między tymi klasami miano, jest trochę skomplikowane funkcje, które zmienia się, jakie dane są zwracane przez tą metodą fasady, stosując ten sam parametr. *

$modernClass->cacheFunctionOne($cache); // easily mocked 

// logic that changes data returned by laravel Cache object function 'someFunction' 

$modernClass->cacheFunctionTwo($cache); // easily mocked with a different mock 

Nasze nowoczesne klasy są łatwe do sprawdzenia, ponieważ podstawowa klasa fasada reprezentowana jest wstrzykiwana do każdej klasy (w tym przykładzie, każda metoda). Oznacza to, że mogę utworzyć dwie oddzielne makiety i wprowadzić je do każdej klasy (metody), aby wyśmiać różne wyniki.

$legacyClass->cacheFunctionOne(); 

// logic that changes data returned by laravel Cache object function 'someFunction' 

$legacyClass->cacheFunctionTwo(); 

W starszych systemach chociaż, to wydają że wyśmiewali fasada jest „globalny”, tak że gdy fasada prowadzony jest w każdej klasie, dokładnie taka sama wartość zwracana jest.

Czy mam rację, myśląc o tym?

* Rozumiem, że ten przykład może wydawać się całkowicie zbędny z punktu widzenia architektury kodu i testowania, ale usuwam całą rzeczywistą funkcjonalność, aby spróbować i podać jakiś "prosty" przykład tego, o co proszę.

Odpowiedz

6

Dependency Injection vs Fasady

Jedną z głównych korzyści wynikających z Dependency Injection jest to, że kod staje się dużo bardziej sprawdzalne po uruchomieniu wstrzykiwanie zależności do metod zamiast instancji/hardcoding je wewnątrz metody. Dzieje się tak dlatego, że możesz przekazywać zależności z wewnętrznych testów jednostkowych i będą one propagować za pomocą kodu.

Patrz: http://slashnode.com/dependency-injection/

Dependency Injection stoi w ostrym kontraście do elewacji. Fasady są statycznymi klasami globalnymi, język PHP nie pozwala na nadpisywanie ani zastępowanie funkcji statycznych na klasach statycznych. Fasady Laravel używają Mockery, aby zapewnić fałszywą funkcjonalność i są ograniczone przez te same fakty, co powyżej.

Problem dotyczący testów integracyjnych może pojawić się w miejscu, w którym ma się ochotę pobrać dane z niezmikrowanej pamięci podręcznej, ale po skorzystaniu z funkcji Facade :: shouldReceive(), funkcja Facade :: get() zostanie zastąpiona wyśmiewaną pamięcią podręczną. Odwrotna też jest prawda. W rezultacie, fasady są nieodpowiednie tam, gdzie przeplatają się połączenia z wyszydzanymi i niezmodyfikowanymi danymi.

Aby przetestować kod przy użyciu różnych zestawów danych, najlepszą metodą byłoby zmodyfikowanie dotychczasowego kodu w celu użycia DI.

Integration Tests

łatwiejsza metoda

Alternatywą jest wywołanie wielokrotnego Fasada :: shouldReceive() z oczekiwaniami na początku testu integracyjnego. Zapewnienie odpowiedniej liczby oczekiwań we właściwej kolejności dla każdego połączenia, które wykonasz w teście integracji. Prawdopodobnie byłby to najszybszy sposób pisania testów z uwzględnieniem istniejącej bazy kodów.

Harder metoda

Podczas wstrzykiwania zależności programuje najlepszych praktyk. Mogłoby się zdarzyć, że twoja baza kodowa ma tyle klas, że zajmie ci to niewiarygodnie dużo czasu. W takim przypadku warto rozważyć kompleksowe testy integracyjne przy użyciu testowej bazy danych z urządzeniami.

Dodatek:

+0

Dziękuję za szczegółowe wyjaśnienie. – myol