2008-11-11 20 views
81

He leído en todas partes sobre lo geniales que son, pero por alguna razón parece que no puedo averiguar exactamente cómo se supone que debo probar algo. ¿Podría alguien publicar un ejemplo de código y cómo lo probarían? Si no es demasiado problema :)¿Cómo escribo las pruebas unitarias en PHP?

+5

para el equilibrio, no hay 2 o 3 marcos de pruebas unitarias para PHP - hay una lista aquí: http://en.wikipedia.org/wiki/List_of_unit_testing_frameworks#PHP – Fenton

Respuesta

22

Hay dos marcos que puede usar para probar la unidad. Simpletest y PHPUnit, que prefiero. Lea los tutoriales sobre cómo escribir y ejecutar pruebas en la página principal de PHPUnit. Es bastante fácil y bien descrito.

31

Hay un tercer "marco", que es mucho más fácil de aprender, incluso más fácil que Simple Prueba, se llama phpt.

Una imprimación se puede encontrar aquí: http://qa.php.net/write-test.php

Editar: acaba de ver su solicitud de código de ejemplo.

Asumamos que tiene la siguiente función en un archivo llamado lib.php:

<?php 
function foo($bar) 
{ 
    return $bar; 
} 
?> 

realmente simple y directo, el parámetro se pasa en, se devuelve. Así que vamos a ver una prueba para esta función, que llamaremos el archivo de prueba foo.phpt:

--TEST-- 
foo() function - A basic test to see if it works. :) 
--FILE-- 
<?php 
include 'lib.php'; // might need to adjust path if not in the same dir 
$bar = 'Hello World'; 
var_dump(foo($bar)); 
?> 
--EXPECT-- 
string(11) "Hello World" 

En pocas palabras, proporcionamos el parámetro con el valor $bar"Hello World" y var_dump() la respuesta de la función llama al foo().

Para ejecutar esta prueba, utilice: pear run-test path/to/foo.phpt

Esto requiere a working install of PEAR en su sistema, lo cual es bastante común en la mayoría de las circunstancias. Si necesita instalarlo, le recomiendo que instale la última versión disponible. En caso de que necesite ayuda para configurarlo, siéntase libre de preguntar (pero proporcione el sistema operativo, etc.).

14

Puede hacer las pruebas unitarias más efectivas cambiando su estilo de codificación para acomodarlo.

Recomiendo navegar por el Google Testing Blog, en particular la publicación en Writing Testable Code.

+6

Creo que mencionó una gran publicación. Comenzar su respuesta con 'Pruebas unitarias no es muy efectiva' casi me hizo rechazar, sin embargo, ser un experto en pruebas ... Posiblemente, reformular de manera positiva alentaría a la gente a leer el artículo. – xtofl

+1

@xtofl lo editó para aumentar un poco la "positividad" :) – icc97

8

Yo rodé por mi cuenta porque no tuve tiempo de aprender otras formas de hacer las cosas, esto tardó unos 20 minutos en redactarse, 10 en adaptarlo para publicarlo aquí.

Unittesting es muy útil para mí.

esto es un poco largo, pero se explica y hay un ejemplo en la parte inferior.

/** 
* Provides Assertions 
**/ 
class Assert 
{ 
    public static function AreEqual($a, $b) 
    { 
     if ($a != $b) 
     { 
      throw new Exception('Subjects are not equal.'); 
     } 
    } 
} 

/** 
* Provides a loggable entity with information on a test and how it executed 
**/ 
class TestResult 
{ 
    protected $_testableInstance = null; 

    protected $_isSuccess = false; 
    public function getSuccess() 
    { 
     return $this->_isSuccess; 
    } 

    protected $_output = ''; 
    public function getOutput() 
    { 
     return $_output; 
    } 
    public function setOutput($value) 
    { 
     $_output = $value; 
    } 

    protected $_test = null; 
    public function getTest() 
    { 
     return $this->_test; 
    } 

    public function getName() 
    { 
     return $this->_test->getName(); 
    } 
    public function getComment() 
    { 
     return $this->ParseComment($this->_test->getDocComment()); 
    } 

    private function ParseComment($comment) 
    { 
     $lines = explode("\n", $comment); 
     for($i = 0; $i < count($lines); $i ++) 
     { 
      $lines[$i] = trim($lines[ $i ]); 
     } 
     return implode("\n", $lines); 
    } 

    protected $_exception = null; 
    public function getException() 
    { 
     return $this->_exception; 
    } 

    static public function CreateFailure(Testable $object, ReflectionMethod $test, Exception $exception) 
    { 
     $result = new self(); 
     $result->_isSuccess = false; 
     $result->testableInstance = $object; 
     $result->_test = $test; 
     $result->_exception = $exception; 

     return $result; 
    } 
    static public function CreateSuccess(Testable $object, ReflectionMethod $test) 
    { 
     $result = new self(); 
     $result->_isSuccess = true; 
     $result->testableInstance = $object; 
     $result->_test = $test; 

     return $result; 
    } 
} 

/** 
* Provides a base class to derive tests from 
**/ 
abstract class Testable 
{ 
    protected $test_log = array(); 

    /** 
    * Logs the result of a test. keeps track of results for later inspection, Overridable to log elsewhere. 
    **/ 
    protected function Log(TestResult $result) 
    { 
     $this->test_log[] = $result; 

     printf("Test: %s was a %s %s\n" 
      ,$result->getName() 
      ,$result->getSuccess() ? 'success' : 'failure' 
      ,$result->getSuccess() ? '' : sprintf("\n%s (lines:%d-%d; file:%s)" 
       ,$result->getComment() 
       ,$result->getTest()->getStartLine() 
       ,$result->getTest()->getEndLine() 
       ,$result->getTest()->getFileName() 
       ) 
      ); 

    } 
    final public function RunTests() 
    { 
     $class = new ReflectionClass($this); 
     foreach($class->GetMethods() as $method) 
     { 
      $methodname = $method->getName(); 
      if (strlen($methodname) > 4 && substr($methodname, 0, 4) == 'Test') 
      { 
       ob_start(); 
       try 
       { 
        $this->$methodname(); 
        $result = TestResult::CreateSuccess($this, $method); 
       } 
       catch(Exception $ex) 
       { 
        $result = TestResult::CreateFailure($this, $method, $ex); 
       } 
       $output = ob_get_clean(); 
       $result->setOutput($output); 
       $this->Log($result); 
      } 
     } 
    } 
} 

/** 
* a simple Test suite with two tests 
**/ 
class MyTest extends Testable 
{ 
    /** 
    * This test is designed to fail 
    **/ 
    public function TestOne() 
    { 
     Assert::AreEqual(1, 2); 
    } 

    /** 
    * This test is designed to succeed 
    **/ 
    public function TestTwo() 
    { 
     Assert::AreEqual(1, 1); 
    } 
} 

// this is how to use it. 
$test = new MyTest(); 
$test->RunTests(); 

Este salidas:

Test: TestOne was a failure 
/** 
* This test is designed to fail 
**/ (lines:149-152; file:/Users/kris/Desktop/Testable.php) 
Test: TestTwo was a success
1

Además de las excelentes sugerencias sobre los marcos de prueba ya dadas, están construyendo su aplicación con uno de los frameworks web PHP que ha automatizado las pruebas incorporadas, tales como Symfony o CakePHP?A veces, tener un lugar para simplemente dejar caer sus métodos de prueba reduce la fricción de arranque que algunas personas asocian con las pruebas automatizadas y TDD.

3

Para pruebas simples Y documentación, php-doctest es bastante agradable y es una manera muy fácil de comenzar ya que no tiene que abrir un archivo por separado. Imagine la siguiente función:

/** 
* Sums 2 numbers 
* <code> 
* //doctest: add 
* echo add(5,2); 
* //expects: 
* 7 
* </code> 
*/ 
function add($a,$b){ 
    return $a + $b; 
} 

Si ahora ejecuta este archivo a través de phpdt (corredor de línea de comandos de PHP-doctest) 1 prueba se llevará a cabo. El doctest está contenido dentro del bloque < code>. Doctest se originó en python y está bien para dar útiles ejemplos & ejecutables sobre cómo se supone que el código funciona. No se puede utilizar exclusivamente porque el código en sí mismo se acumularía con casos de prueba, pero he descubierto que es útil junto con una biblioteca tdd más formal: yo uso phpunit.

Esta primera respuesta here lo resume muy bien (no es unidad frente a doctest).

+1

¿no ensucia un poco la fuente? –

+0

puede. solo debe usarse para pruebas sencillas simples. también funciona como documentación. si necesita más prueba de unidad de uso. – Sofia

4

Obtenga PHPUnit. Es muy fácil de usar.

A continuación, comience con aserciones muy simples. Puedes hacer mucho con AssertEquals antes de entrar en cualquier otra cosa. Esa es una buena manera de mojarse los pies.

Es posible que también desee intentar escribir su prueba primero (ya que le dio a su pregunta la etiqueta TDD) y luego escriba su código. Si no has hecho esto antes, es una revelación.

require_once 'ClassYouWantToTest'; 
require_once 'PHPUnit...blah,blah,whatever'; 

class ClassYouWantToTest extends PHPUnit...blah,blah,whatever 
{ 
    private $ClassYouWantToTest; 

    protected function setUp() 
    { 
     parent::setUp(); 
     $this->ClassYouWantToTest = new ClassYouWantToTest(/* parameters */); 
    } 

    protected function tearDown() 
    { 
     $this->ClassYouWantToTest = null; 
     parent::tearDown(); 
    } 

    public function __construct() 
    { 
     // not really needed 
    } 

    /** 
    * Tests ClassYouWantToTest->methodFoo() 
    */ 
    public function testMethodFoo() 
    { 
     $this->assertEquals(
      $this->ClassYouWantToTest->methodFoo('putValueOfParamHere), 'expectedOutputHere); 

    /** 
    * Tests ClassYouWantToTest->methodBar() 
    */ 
    public function testMethodFoo() 
    { 
     $this->assertEquals(
      $this->ClassYouWantToTest->methodBar('putValueOfParamHere), 'expectedOutputHere); 
} 
2

phpunit es prácticamente el marco de pruebas de unidades de facto para php. también hay DocTest (disponible como paquete PEAR) y algunos otros. php se prueba para regresiones y similares a través de phpt tests que también se puede ejecutar a través de pera.

2

Las pruebas de codecepción son muy parecidas a las pruebas de unidades comunes, pero son mucho más potentes en los casos en los que se requieren burlas y trozos.

Aquí está la prueba del controlador de muestra. Observe cómo fácilmente se crean los talones. Qué tan fácil es verificar el método invocado.

<?php 
use Codeception\Util\Stub as Stub; 

const VALID_USER_ID = 1; 
const INVALID_USER_ID = 0; 

class UserControllerCest { 
public $class = 'UserController'; 


public function show(CodeGuy $I) { 
    // prepare environment 
    $I->haveFakeClass($controller = Stub::makeEmptyExcept($this->class, 'show')); 
    $I->haveFakeClass($db = Stub::make('DbConnector', array('find' => function($id) { return $id == VALID_USER_ID ? new User() : null))); }; 
    $I->setProperty($controller, 'db', $db); 

    $I->executeTestedMethodOn($controller, VALID_USER_ID) 
     ->seeResultEquals(true) 
     ->seeMethodInvoked($controller, 'render'); 

    $I->expect('it will render 404 page for non existent user') 
     ->executeTestedMethodOn($controller, INVALID_USER_ID) 
     ->seeResultNotEquals(true) 
     ->seeMethodInvoked($controller, 'render404','User not found') 
     ->seeMethodNotInvoked($controller, 'render'); 
} 
} 

También hay otras cosas interesantes. Puede probar el estado de la base de datos, el sistema de archivos, etc.

1

Demasiado para volver a publicarlo aquí, pero aquí hay un great article sobre el uso de phpt. Cubre una serie de aspectos alrededor de phpt que a menudo se pasan por alto, por lo que podría valer la pena leer para ampliar su conocimiento de php más allá de simplemente escribir una prueba. ¡Afortunadamente el artículo también discute las pruebas de escritura!

Los principales puntos de discusión

  1. Descubre como marginalmente documentado los aspectos del trabajo de PHP (o casi cualquier parte, para el caso)
  2. escribir pruebas unitarias simples para su propio código PHP
  3. pruebas de escritura como parte de una extensión o para transmitir una posible falla a los internos o grupos de control de calidad
1

Sé que hay mucha información aquí, pero como esto todavía aparece en Google búsquedas también podría agregar Chinook Test Suite a la lista. Es un marco de prueba simple y pequeño.

Puede probar fácilmente sus clases con él y también crear objetos simulados. Ejecuta las pruebas a través de un navegador web y (aún no) a través de una consola. En el navegador puede especificar qué clase de prueba o incluso qué método de prueba ejecutar. O simplemente puede ejecutar todas las pruebas.

Una captura de pantalla de la página de GitHub:

Chinook Unit Test framework

Lo que me gusta es la forma en que usted afirma pruebas. Esto se hace con las llamadas "afirmaciones fluidas". Ejemplo:

$this->Assert($datetime)->Should()->BeAfter($someDatetime); 

y la creación de objetos de imitación es una brisa también (con una fluidez como la sintaxis):

$mock = new CFMock::Create(new DummyClass()); 
$mock->ACallTo('SomeMethod')->Returns('some value'); 

De todos modos, más información se puede encontrar en la página de GitHub con un código de ejemplo, así:

https://github.com/w00/Chinook-TestSuite

Cuestiones relacionadas