symfonytestslistener - Mocking la petición y sesión de Symfony2 en PHPUnit
symfony test security (5)
Es difícil imaginar una situación en la que deba lidiar con los parámetros GET / POST dentro de una clase probada por unidades. Haga que el controlador trate con las solicitudes y sesiones de HTTP (es más o menos para lo que están ahí), y pase las variables a las clases relevantes para lidiar con el resto.
Una vez dicho esto, la respuesta de Kevin es una posible solución si quieres ir por esa ruta.
Tengo una clase que requiere el servicio Symfony2
@request_stack
que devuelve una instancia de Symfony/Component/HttpFoundation/RequestStack
. Lo uso para recuperar los valores POST y GET.
Y también mi clase usa Symfony/Component/HttpFoundation/Session
de Request->getSession()
que llama para obtener la sesión actual.
En este momento, mi clase tiene un método que se ve así:
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);
}
}
Necesito ser capaz de:
- Mock
RequestStack
. - Obtener
Request
deRequestStack
- Obtenga la
Session
deReques
t;
Con todo eso, ¿cómo puedo probar que MyClass
establezca con éxito el valor esperado en la sesión?
No todo el código vale la prueba unitaria. Por lo general, este es un indicador de que su código podría simplificarse. Cuando su código de prueba unitaria es algo complejo, las pruebas pueden convertirse en una carga y, en estos casos, sería mejor realizar una prueba de integración de extremo a extremo. Tampoco está claro en tu ejemplo cómo tu clase obtiene RequestStack
así que RequestStack
que se ha inyectado en __construct
.
Dicho esto, así es como probarías ese código:
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();
}
Esto debería darle un punto de partida, pero tenga cuidado con tener un muro de simulacros en sus pruebas porque los cambios muy pequeños en los detalles de implementación pueden hacer que sus pruebas fallen aun cuando el comportamiento sea correcto y esto es algo que desea evitar tanto como Es posible que las pruebas no sean costosas de mantener.
Editar: pensé un poco más sobre tu pregunta y me di cuenta de que normalmente puedes inyectar la sesión como una dependencia. Si eso es posible en su caso de uso, simplificaría mucho las pruebas.
De acuerdo con esto: http://api.symfony.com/2.4/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.html
Tengo que trabajar algo como lo siguiente:
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'');
}
Solo una prueba por tipo de objeto en mi caso, ya que solo estoy probando si el nombre de la sesión es correcto y recupero / almacena el objeto correctamente en la sesión. CompanyMapper
clase CompanyMapper
usa la sesión para almacenar el objeto de la compañía entre otras funciones relacionadas con la sesión / aplicación.
No necesita burlarse de RequestStack
, es una clase súper simple. Solo puede crear una solicitud falsa y presionarla. También puedes burlarte de la sesión.
// 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.
Pero en términos de pruebas, tener que meterse con las partes internas de una clase no es una buena señal. Tal vez pueda crear una clase de controlador para encapsular la lógica que necesita de la pila de solicitudes para que pueda realizar pruebas más fácilmente.
Cualquiera que venga de Google como yo quiere saber cómo simular el contenido de la solicitud, es tan simple como:
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());
}
}
Como puede ver, puede ejecutar un controlador real que usa $request->getContent()
Espero que esto ayude a alguien.