El framework symfony siempre ha sido empaquetado con un framework de pruebas funcionales y es, sin duda, una de sus principales fortalezas.

¿Qué es una prueba funcional? El objetivo de las pruebas funcionales es poner a prueba la integración de todas las capas de tu aplicación: desde el routing al controlador, plantillas, y las llamadas a la base de datos. Estas no reemplazan las pruebas unitarias.

La única cosa que no puede probar fácilmente es el código JavaScript embebido en tus plantillas. Por supuesto, puedes utilizar una herramienta como selenium para esto. Pero la buena noticia es que el framework de pruebas funcionales puede probar "algunos" códigos JavaScript como las llamadas Ajax.

Para hacer el trabajo, el framework de pruebas funcionales simula un navegador. No necesita un servidor web que conozca symfony y de como genera una respuesta basada en un peticion. Esto permite una fácil y profunda inspección del estado de tu aplicación después de cada solicitud. Puedes, por supuesto, inspeccionar los objetos del núcleo de symfony como la respuesta o la sesiones de usuario, pero también tu propio código al igual que el modelo.

(N. del T.: inspección = introspección )

Cada versión de symfony hace del framework de pruebas funcionales aun mejor. Hoy, les mostrare todos las bondades que agregamos a la versión symfony 1.2. Preparate para asombrarte!

Desconectando

Todo el mundo sabe que me gusta mucho testear. Tambien me gusta refactorear código viejo para hacerlo mejor. Para symfony 1.2, he refactoreado las clases del navegador (sfBrowser) y del navegador de pruebas (sfTextBrowser) para hacerlas mas flexibles y configurables.

A partir de symfony 1.2, el framework de pruebas funcionales esta hecho de varias capas reusables e independientes.

El mayor cambio es la introducción de testers. Los Testers son objetos que saben como probar una capa específica de tu aplicación. Symfony viene hecho con testers para la petición, la respuesta, el usuario, el cache de la Vista, formularios, y Propel.

Un cambio menor es la introducción de sfTestFunctional class, la cual se basa en sfBrowser object para probar tu aplicación y manejar todos los testers registrados.

Aqui una típica prueba funcional:

$browser = new sfTestFunctional(new sfBrowser());



 
$browser->
  get('/')->
  // do some tests
;
 

Para mantener la compatibilidad para atras con symfony 1.0 y 1.1, aun puedes utilizar la ahora desestimada clase sfTestBrowser :

$browser = new sfTestBrowser();
 
$browser->

  get('/')->
  // do some tests
;
 

Testers

Entonces, todo el testing es en realidad hecho por las clases tester. Un tester sabe como probar una parte específica de tu aplicación.

Los testers remplazan todos los método que usabas, como checkResponseElement() o isRequestParameter(). Por supuesto, estos métodos aun estan disponibles para mantener la compatibilidad hacia atrás (el archivo UPGRADE_TO_1_2 tiene una tabla que referencia todos los métodos viejos y sus tester equivalentes).

Aquí esta un ejemplo simple que demuestra como remplazar la llamada isRequestParameter() usando el tester:

// before symfony 1.2
$browser->
  get('/')->
 
  isRequestParameter('module', 'foo')->

  checkResponseElement('h1', 'foo')
;
 
// as of symfony 1.2
$browser->
  get('/')->

 
  with('request')->isParameter('module', 'foo')->
  checkResponseElement('h1', 'foo')

;
 

El with('request') interrumpe el contexto del flujo de la interfaz para el objeto tester request para la próxima llamada. Entonces, el método isParameter() es un método sfTesterRequest.

También puedes crear un bloque de llamadas en el que el contexto es el Objeto tester:

$browser->
  get('/')->
 
  with('request')->begin()->

    isParameter('module', 'foo')->
    isParameter('action', 'index')->

  end()->
 
  checkResponseElement('h1', 'foo')
;


 

Todas las llamadas del método entre begin()y end() son llamadas contra el actual object tester.

Veamos los métodos de prueba provistos por las clases tester.

Request Tester

El request tester es definido en la clase sfTesterRequest y contiene los siguientes métodos:

Método
Descripción
isParameter Prueba el parámetro request
isMethod Prueba el método request
isFormat Prueba el formato request
hasCookie Prueba de si la petición ha dado una cookie
isCookie Prueba el valor de una cookie
$browser->
  get('/')->

 
  with('request')->begin()->

    isParameter('module', 'foo')->

    isMethod('get')->
    isFormat('html')->

    hasCookie('foo')->

    isCookie('foo', 'bar')->
  end()

;

 

Response Tester

El response tester es definido en la clase sfTesterResponse y contiene los siguientes métodos:

Método Descripción
isStatusCode Prueba el código de estado del response
contains Prueba el contenido del response con una simple expresión regular
isHeader Prueba el valor de una cabecera dada
checkElement Verifica el valor de un selector CSS3
$browser->
  get('/')->

 
  with('response')->begin()->
    isStatusCode(200)->

    contains('foo')->
    isHeader('Content-Type', 'text/plain')->

    checkElement('ul.foo li:last', '/foo/')->

  end()
;

 

View cache tester

El view cache tester es definido en la clase sfTesterViewCache y contiene los siguientes métodos:

Método Descripción
isCached Comprueba si una página/acción está en el caché
isUriCached Comprueba si un determinado URI (puede ser un partial) está en el caché
$browser->

  get('/')->

 
  with('view_cache')->begin()->

    isCached(true)->

    isUriCached('@sf_cache_partial?module=foo&action=_partial&sf_cache_key=some_cache_key')->

  end()

;
 

User tester

El user tester es definido en la clase sfTesterUser y contiene los siguientes métodos:

Método Descripción
isCulture Prueba la cultura del usuario
isAuthenticated Comprueba que el usuario esta autenticado
hasCredential Comprueba por una credencial de usuario
isAttribute Prueba el valor de un determinado atributo
isFlash Prueba el valor de una variable flash
$browser->
  get('/')->

 
  with('user')->begin()->

    isCulture('fr')->

    isAuthenticated(true)->
    hasCredential('admin')->

    isAttribute('sfguard_user_id', '3')->

    isFlash('notice', '/foo/')->

  end()
;
 

Form Tester

Ha llegado la hora de descubrir algunos de los nuevos y sexy testers!

El form tester es definido en la clase sfTesterForm. Este sabe si un form se ha utilizado en la petición anterior, tiene una referencia al mismo objeto form, y te permite que inspeccionarlo.

Método Descripción
hasErrors Compruebe si el formulario enviado tiene algún error
isError Prueba el valor de un error para un campo determinado
hasGlobalError Igual que isError pero para los errores globales

El metodo isError() tiene el mismo tipo segundo argumento como el metodo checkResponseElement().

$browser->
  click('save', array(...))->

  with('form')->begin()->
    hasErrors()->

    hasGlobalError('The login and password does not match.')->
    isError('name', 'Required.')->

    isError('name', '/Required/')->

    isError('name', '!/Invalid/')->

    isError('name')->
    isError('name', false)->

    isError('name', 1)->
  end()
;
 

Propel Tester

Aquí esta otro gran tester: el propel tester.

No reemplaza las pruebas de la respuesta HTML pero es un medio para también comprobar las cosas que no se muestran en el navegador pero no por eso menos importante para testear (por ejemplo si la fecha y hora del last_connection para un usuario ha sido actualizado, o si el número de vistas para un artículo se ha incrementado, ...).

El propel tester es definido en sfTesterPropel en el Propel plugin y debe ser registrado antes de ser usado:

$browser->setTester('propel', 'sfTesterPropel');
 

Despues que el tester está registrado, puedes utilizarlo en tus pruebas:

$browser->

  post('/')->
  with('propel')->begin()->

    check('Article', array('title' => 'foo'), false)->

    check('Article', array('title' => '!foo'), false)->

    check('Article', array(), 4)->
    check('Article', array('title' => '%foo%'), true)->

    check('Article', array('title' => '!%foo%'))->

    check('Article', $criteria)->

  end()
;

 

El propel tester sólo ofrece un método: check().Este método se comporta de manera diferente segun los argumentos que le pases a el:

Extender o crear un tester

El uso testers tiene varias ventajas:

Extender un tester de la fabrica de symfony

Si deseas agregar algunos métodos para un tester existente, necesitas crear una clase que herede de un tester de fabrica y re-registrarlo con tu propio nombre de clase:

class ApplicationTesterRequest extends sfTesterRequest

{

  // add some tester methods

}
 
// in your functional tests
$browser->setTester('request', 'ApplicationTesterRequest');


 

Si necesita reemplazar un montón de testers de fabrica, puedes utilizar el metodo setTesters:

$browser->setTesters(array(
  'request'  => 'ApplicationTesterRequest',
  'response' => 'ApplicationTesterResponse',



));
 

Un método tester puede hacer cualquier cosa que gustes pero siempre debe finalizar con el siguiente código para que el flujo de la interfaz funcione correctamente:

return $this->getObjectToReturn();
 

En tu método, tienes acceso a varios objetos:

Crear un nuevo tester

También se puede crear una nueva clase tester registrandola con un nombre único:

$browser->setTester('my_tester', 'myTester');
 

Una clase tester hereda de sfTester y debe implementar los métodos siguientes:

Se fluido

Cuando escribes un montón de tests funcionales para un determinado módulo, a veces es útil disponer de alguna información visual acerca de lo que se está haciendo. Los nuevos testers añade un nuevo nivel de indentación y hace tests mas legibles.

También, hay un nuevo método info() que produce algún texto para ayudar a categorizar tus tests:

$browser->
  info('First scenario: Form with errors')->
  // ... some tests
  info('Second scenario: Valid form submission')->

  // ... some more tests
;
 

info in the browser

Depurando tests

Cuando un problema se produce en un test funcional, el HTML trasladado al navegador ayudar a diagnosticar la causa. A partir de symfony 1.2, esto es bastante fácil para mostrar el HTML generado sin interrumpir el estilo fluido de la interfaz:

$browser->

  get('/a_uri_with_an_error')->
  with('response')->debug()->

  // some tests that won't be executed
;
 

El método debug() devolvera las cabeceras y el contenido e interrumpira el flujo del navegador.

El mismo método debug() existe para el form tester y devuelve los valores enviados y los errores del  form si los hay:

$browser->
  post('/post_to_a_form_with_some_errors')->
  with('form')->debug()->

  // some tests that won't be executed
;
 

debug

Esto es todo por hoy. Nunca ha sido más fácil testear tus aplicaciones. Espero que el nuevo framework de testing pueda convencerte que no es dificil y que puede salvar tu día.

En cuanto a las nueva web debug toolbar, si creas nuevos testers, no dudes empaquetarlos como un plugin.



La presente, es una traducción al castellano realizada por Roberto G. Puentes Diaz, sobre el artículo new-in-symfony-1-2-test-your-applications de Fabien Potencier.