2014-05-23 9 views
7

Mam klasę wymagającą usługi Symfony2@request_stack, która zwraca instancję z Symfony\Component\HttpFoundation\RequestStack. Używam go do pobierania wartości POST i GET.Kpiny z prośby i sesji Symfony2 w PHPUnit

A także moja klasa używa Symfony\Component\HttpFoundation\Session z Request->getSession(), którą wywołuje, aby uzyskać bieżącą sesję.

Teraz moja klasa ma metodę, która wygląda mniej więcej tak:

class MyClass { 
    public function doSomething() { 
     //Get request from request stack. 
     $Request = $this->RequestStack->getCurrentRequest(); 

     //Get a variable from request 
     $var = $Request->request->get('something'); 
     //Processes $var into $someprocessedvar and lets say it's equal to 3. 
     //Set value to session. 
     $this->Request->getSession()->set('somevar', $someprocessedvar); 
    } 
} 

muszę być w stanie:

  1. Mock RequestStack.
  2. Get Request od RequestStack
  3. Get Session z Reques t;

Z tym wszystkim powiedziane, jak mogę przetestować, czy MyClass pomyślnie ustawić oczekiwaną wartość w sesji?

Odpowiedz

0

Zgodnie z tym: http://api.symfony.com/2.4/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.html

mam pracować coś jak następuje:

public function testCompanySession() 
{ 
    $Request = new Request(); 
    $Request->setSession(
     new Session(new MockArraySessionStorage()) 
    ); 

    $CompanySessionMapper = new CompanyMapper($Request); 

    $Company = new Company(); 

    $Company->setName('test'); 

    $CompanySessionMapper->set($Company); 

    $Company = new Company(); 

    $CompanySessionMapper->get($Company); 

    $this->assertEquals($Company->getName(), 'test'); 
} 

tylko jedno badanie per typu obiektu w moim przypadku, ponieważ mam testowanie tylko wtedy, gdy sesja jest poprawna nazwa i pobieranie/przechowywanie obiektu poprawnie w sesji. Klasa CompanyMapper używa sesji do przechowywania obiektu firmy wśród innych funkcji związanych z sesją/aplikacją.

7

Nie wszystkie kody są warte testów jednostkowych. Zwykle jest to wskaźnik, że Twój kod może zostać uproszczony. Kiedy jednostka kodu testowego jest nieco skomplikowana, testy mogą stać się ciężarem i zwykle lepiej byłoby w takich przypadkach przeprowadzić integrację testu od krawędzi do krawędzi. W twoim przykładzie nie jest też jasne, w jaki sposób twoja klasa dostaje RequestStack, więc założę się, że została ona wprowadzona w __construct.

Z powiedział, że oto w jaki sposób przetestować ten kod:

protected function setUp() 
{ 
    $this->requestStack = $this->getMock('Fully-qualified RequestStack namespace'); 

    $this->SUT = new MyClass($this->requestStack); 
}  

/** @test */ 
public function it_should_store_value_in_the_session() 
{ 
    $value = 'test value'; 

    $request = $this->getMock('Request'); 
    $request->request = $this->getMock('ParameterBag'); 
    $session = $this->getMock('Session'); 

    $this->requestStack 
     ->expects($this->atLeastOnce()) 
     ->method('getCurrentRequest') 
     ->will($this->returnValue()); 

    $request->request 
     ->expects($this->atLeastOnce()) 
     ->method('get') 
     ->with('something') 
     ->will($this->returnValue($value)); 

    $request 
     ->expects($this->once()) 
     ->method('getSession') 
     ->will($this->returnValue($session)); 

    $session 
     ->expects($this->once()) 
     ->method('set') 
     ->with('somevar', $value); 

    $this->SUT->doSomething(); 
} 

To powinno dać punkt wyjścia, ale uwaga posiadające ściany z mocks w badaniach, ponieważ bardzo małe zmiany do szczegółów realizacji może powodować Twoje testy się nie udają, mimo że zachowanie jest nadal poprawne i jest to coś, czego chcesz uniknąć w jak największym stopniu, więc testy nie są drogie w utrzymaniu.

Edytuj: Pomyślałem trochę o twoim pytaniu i zdałem sobie sprawę, że zazwyczaj możesz wstrzyknąć Sesję jako zależność. Jeśli jest to możliwe w twoim przypadku użycia, znacznie uprościłoby to testy.

+0

Napisałem własną odpowiedź, jeśli chcesz to sprawdzić, ale zaakceptowałem twoją odpowiedź, ponieważ zgadzam się z tobą w przeważającej części. – Tek

1

Trudno sobie wyobrazić sytuację, w której musiałbyś radzić sobie z parametrami GET/POST w klasie z testami jednostkowymi. Czy kontroler radzi sobie z żądaniami HTTP i sesjami (to jest to, o co w ogóle chodzi) i przekazuje zmienne do odpowiednich klas, aby poradzić sobie z resztą.

Mimo to odpowiedź Kevina jest możliwym rozwiązaniem, jeśli chcesz podążać tą trasą.

+0

Jest to klasa do odwzorowywania jednostek na sesje. To nie tylko pobieranie danych, ale także ustawienie sesji. Jest o wiele bardziej przydatny do wielokrotnego użytku niż posiadanie całej tej logiki w kontrolerze. – Tek

+0

@tek w tym przypadku nie możesz po prostu kpić z sesji i jakichkolwiek innych zależności, których twoja klasa potrzebuje. Kontener nigdy nie powinien być używany w teście jednostkowym, ponieważ jak możesz kpić z zależności rozwiązanych z pojemnika? – echochamber

1

Nie musisz kpić z RequestStack, to super prosta klasa. Możesz po prostu utworzyć fałszywą prośbę i wepchnąć ją do niej. Możesz także kpić z sesji.

// you can overwrite any value you want through the constructor if you need more control 
$fakeRequest = Request::create('/', 'GET'); 

$fakeRequest->setSession(new Session(new MockArraySessionStorage())); 
$requestStack = new RequestStack(); 
$requestStack->push($fakeRequest); 
// then pass the requestStack to your service under test. 

Ale jeśli chodzi o testowanie, to niezanieczyszczanie wnętrzem klasy nie jest dobrym znakiem. Być może możesz utworzyć klasę obsługi, aby hermetyzować logikę, której potrzebujesz, ze stosu żądań, abyś mógł łatwiej testować.

0

Każdy pochodzące z Google jak ja chce wiedzieć, jak wyśmiewać treść żądania, to jest tak proste, jak:

use AppBundle\Controller\DefaultController; 
use Symfony\Component\HttpFoundation\Request; 
use PHPUnit\Framework\TestCase; 

class DefaultControllerTest extends TestCase 
{ 
    //@dataProvider? 
    public function testWithMockedRequest() 
    { 
     //create a request mock 
     $request = $this 
      ->getMockBuilder(Request::class) 
      ->getMock(); 

     //set the return value 
     $request 
      ->expects($this->once()) 
      ->method('getContent') 
      ->will($this->returnValue('put your request data here')); 

     //create your controller 
     $controller = new DefaultController(); 

     //get the Response based on your Request 
     $response = $controller->myRequestAction($request); 

     //assert! 
     $this->assertEquals(200, $response->getStatusCode()); 
    } 
} 

Jak widać można wykonać prawdziwy kontroler, który wykorzystuje $request->getContent()

I mam nadzieję, że to pomoże komuś.