2014-05-23 11 views
7

Ho una classe che richiede il servizio Symfony2@request_stack che restituisce un'istanza di Symfony\Component\HttpFoundation\RequestStack. Lo uso per recuperare i valori POST e GET.Mocking richiesta e sessione di Symfony2 in PHPUnit

E anche la mia classe utilizza Symfony\Component\HttpFoundation\Session da Request->getSession() che chiama per ottenere la sessione corrente.

In questo momento la mia classe ha un metodo che sembra qualcosa di simile:

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); 
    } 
} 

ho bisogno di essere in grado di:

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

Con tutto ciò che è stato detto, come posso verificare che MyClass abbia impostato correttamente il valore previsto nella sessione?

risposta

0

Secondo questo: http://api.symfony.com/2.4/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.html

ho avuto modo di lavorare qualcosa come il seguente:

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'); 
} 

una sola prova per ogni tipo di oggetto nel mio caso dato che sto solo testando se il nome della sessione è corretto e recuperare/archiviare l'oggetto correttamente nella sessione. La classe CompanyMapper utilizza la sessione per archiviare l'oggetto società tra le altre funzioni relative a sessione/applicazione.

7

Non tutto il codice vale la pena testare. Di solito questo è un indicatore che il tuo codice potrebbe essere semplificato. Quando il codice di test unitario è alquanto complesso, i test possono diventare un onere e normalmente sarebbe meglio fare un'integrazione del test edge-to-edge in questi casi. Inoltre, non è chiaro nel tuo esempio come la tua classe ottiene il RequestStack quindi presumo che sia stato iniettato in __construct.

Detto questo ecco come si potrebbe verificare che il codice:

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(); 
} 

Questo dovrebbe dare un punto di partenza ma attenzione con un muro-di mock nei test perché molto piccole modifiche ai dettagli di implementazione può causare i test falliscono anche se il comportamento è ancora corretto e questo è qualcosa che si vuole evitare il più possibile, quindi i test non sono costosi da mantenere.

Modifica: ho pensato un po 'di più alla tua domanda e mi sono reso conto che in genere è possibile iniettare la Sessione come dipendenza. Se ciò è possibile nel tuo caso d'uso, semplificherebbe molto le prove.

+0

Ho scritto la mia risposta, se vuoi controllare, ma ho accettato la tua risposta perché sono d'accordo con te per la maggior parte. – Tek

1

È difficile immaginare una situazione in cui dovresti avere a che fare con i parametri GET/POST all'interno di una classe testata dall'unità. Chiedi al controller di gestire le richieste e le sessioni HTTP (è praticamente il motivo per cui sono lì) e passa le variabili nelle classi pertinenti per occuparsi del resto.

Detto questo, la risposta di Kevin è una possibile soluzione se si vuole percorrere questa strada.

+0

È una classe per mappare le entità alle sessioni. Non si tratta solo di ottenere dati, ma anche di impostarli su sessioni. È molto più riutilizzabile che avere tutta quella logica nel controller. – Tek

+0

@tek in tal caso non puoi semplicemente prendere in giro la sessione e qualunque altra dipendenza della tua classe abbia bisogno. Il contenitore non deve mai essere utilizzato in un test di unità, in quanto è possibile risolvere le dipendenze risolte dal contenitore? – echochamber

1

Non è necessario prendere in giro RequestStack, è una classe super semplice. Puoi semplicemente creare una richiesta falsa e inviarla ad essa. Puoi anche prendere in giro la sessione.

// 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. 

Ma in termini di test, doversi scontrare con gli interni di una classe non è un buon segno. Forse puoi creare una classe di gestore per incapsulare la logica necessaria dallo stack di richieste in modo da poter testare più facilmente.

0

chi proviene da Google, come me, vuole sapere come per deridere contenuti richiesta, è semplice come:

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()); 
    } 
} 

Come si può vedere è possibile eseguire un vero e proprio regolatore che utilizza $request->getContent()

I spero che questo aiuti qualcuno.