2009-05-22 4 views
28

Estoy casi convencido de los beneficios de las pruebas unitarias, y me gustaría comenzar a aplicar el concepto a una gran base de código existente escrita en PHP. Menos del 10% de este código está orientado a objetos.¿Cómo escribo pruebas unitarias en PHP con una base de código de procedimiento?

He examinado varios marcos de prueba de unidades (PHPUnit, SimpleTest y phpt). Sin embargo, no he encontrado ejemplos para ninguno de estos que prueben el código de procedimiento. ¿Cuál es el mejor marco para mi situación? ¿Hay algún ejemplo de prueba unitaria de PHP utilizando código no OOP?

Respuesta

34

Puede probar la unidad de procedimiento de PHP, no hay problema. Y definitivamente no estás de suerte si tu código está mezclado con HTML.

En el nivel de prueba de aplicación o aceptación, su PHP procedimental probablemente depende del valor de los superglobales ($_POST, $_GET, $_COOKIE, etc.) para determinar el comportamiento y termina incluyendo un archivo de plantilla y escupiendo el resultado.

Para realizar pruebas de nivel de aplicación, puede establecer los valores superglobales; inicia un buffer de salida (para evitar que un montón de html inunde tu pantalla); llamar a la página; afirmar contra cosas dentro del buffer; y trash la memoria intermedia al final. Así, se podría hacer algo como esto:

public function setUp() 
{ 
    if (isset($_POST['foo'])) { 
     unset($_POST['foo']); 
    } 
} 

public function testSomeKindOfAcceptanceTest() 
{ 
    $_POST['foo'] = 'bar'; 
    ob_start(); 
    include('fileToTest.php'); 
    $output = ob_get_flush(); 
    $this->assertContains($someExpectedString, $output); 
} 

Incluso para enormes "marcos" con mucha incluye, este tipo de pruebas le dirá si tiene características de nivel de aplicación funcionando o no. Esto va a ser muy importante a medida que empiece a mejorar su código, porque incluso si está convencido de que el conector de la base de datos aún funciona y se ve mejor que antes, querrá hacer clic en un botón y ver que, sí, todavía puede iniciar sesión y cerrar sesión en la base de datos.

En los niveles inferiores, existen pequeñas variaciones dependiendo del alcance de la variable y si las funciones funcionan por efectos secundarios (devolviendo verdadero o falso) o devuelven el resultado directamente.

¿Las variables se transmiten explícitamente, como parámetros o matrices de parámetros entre funciones? ¿O las variables se establecen en muchos lugares diferentes y se pasan implícitamente como globales? Si es el caso explícito (bueno), puede probar la función de una función (1) incluyendo el archivo que contiene la función, luego (2) alimentar los valores de prueba de función directamente, y (3) capturar la salida y afirmar contra ella. Si usa globales, solo tiene que tener mucho cuidado (como se indica arriba, en el ejemplo de $ _POST) para anular cuidadosamente todos los valores globales entre las pruebas. También es especialmente útil mantener las pruebas muy pequeñas (5-10 líneas, 1-2 afirmaciones) cuando se trata de una función que empuja y tira de muchos elementos globales.

Otro problema básico es si las funciones funcionan devolviendo la salida, o alterando los parámetros pasados, devolviendo verdadero/falso en su lugar. En el primer caso, la prueba es más fácil, pero una vez más, es posible en ambos casos:

// assuming you required the file of interest at the top of the test file 
public function testShouldConcatenateTwoStringsAndReturnResult() 
{ 
    $stringOne = 'foo'; 
    $stringTwo = 'bar'; 
    $expectedOutput = 'foobar'; 
    $output = myCustomCatFunction($stringOne, $stringTwo); 
    $this->assertEquals($expectedOutput, $output); 
} 

en el caso grave, donde funciona su código por los efectos secundarios y devuelve verdadero o falso, todavía se puede probar con bastante facilidad :

/* suppose your cat function stupidly 
* overwrites the first parameter 
* with the result of concatenation, 
* as an admittedly contrived example 
*/ 
public function testShouldConcatenateTwoStringsAndReturnTrue() 
    { 
     $stringOne = 'foo'; 
     $stringTwo = 'bar'; 
     $expectedOutput = 'foobar'; 
     $output = myCustomCatFunction($stringOne, $stringTwo); 
     $this->assertTrue($output); 
     $this->Equals($expectedOutput, $stringOne); 
    } 

Espero que esto ayude.

+0

Esto no es justo si el código que está probando está plagado de sentencias 'exit;' :( –

+3

@YarekT Ninguna prueba puede tener un buen rendimiento si el código está lleno de sentencias 'exit' o' die'. La forma más comprobable de manejar la finalización esperada del script es lanzar una excepción y registrar un manejador de excepción personalizado que sea lo suficientemente inteligente como para ignorar el tipo de excepción personalizada cuando se encuentre. Luego puede probar que se produce la excepción esperada. Por supuesto, una aplicación bien diseñada realmente no debería Nunca he necesitado 'exit' o' die' después de la fase de arranque de todos modos. – rdlowrey

1

Se podría tratar de incluir su código no-oop en una clase de prueba utilizando

require_once 'your_non_oop_file.php' # Contains fct_to_test() 

Y con PHPUnit a definir su función de prueba:

testfct_to_test() { 
    assertEquals(result_expected, fct_to_test(), 'Fail with fct_to_test'); 
} 
+0

Parece una solución interesante, pero ¿qué pasa si la mayoría de su lógica no incluye funciones sino bucles y condicionales simples? –

+5

Luego tienes una gran cantidad de código de spaghetti. –

+0

Supuse que Travis no tiene código php con html. –

6
pruebas

qué unidad hacen bien, y lo deberías usarlos, es cuando tienes un código que le das un número de entradas, y esperas obtener una cantidad de salidas atrás. La idea es que, cuando agregue funcionalidad más adelante, pueda ejecutar sus pruebas y asegurarse de que todavía esté realizando la funcionalidad anterior de la misma manera.

lo tanto, si usted tiene una base de código de procedimiento, que puede lograr este llamado a sus funciones en los métodos de ensayo

require 'my-libraries.php'; 
class SomeTest extends SomeBaseTestFromSomeFramework { 
    public function testSetup() { 
     $this->assertTrue(true); 
    } 

    public function testMyFunction() { 
     $output = my_function('foo',3); 

     $this->assertEquals('expected output',$output); 
    } 
} 

Este truco con bases de código PHP es, a menudo el código de la biblioteca va a interferir con el funcionamiento de su marco de prueba, ya que su base de código y los marcos de prueba tendrán muchos códigos relacionados con la configuración de un entorno de aplicación en un navegador web (sesión, variables globales compartidas, etc.). Espere pasar algún tiempo llegando a un punto donde pueda incluir su código de biblioteca y ejecutar una prueba simple (la función testSetup arriba).

Si su código no tiene funciones, y es solo una serie de archivos PHP que generan páginas HTML, no tiene suerte. Su código no puede separarse en unidades distintas, lo que significa que las pruebas unitarias no le serán de mucha utilidad. Sería mejor pasar su tiempo en el nivel de "prueba de aceptación" con productos como Selenium y Watir. Estos le permitirán automatizar un navegador, y luego verificar las páginas en busca de contenido como ubicaciones específicas/en formularios.

+0

Definitivamente puedo separar mi código en distintas unidades. Tu ejemplo parece prometedor, supongo que esto no está utilizando ningún marco particular. –

+0

Sí, eso es solo un pseudo código de prueba, pero las pruebas de SImpleTest y las pruebas de PHPUnit se parecen notablemente. –

Cuestiones relacionadas