Ho letto ovunque su quanto siano fantastici, ma per qualche motivo non riesco a capire esattamente come dovrei provare qualcosa. Qualcuno potrebbe forse pubblicare un pezzo di codice di esempio e come lo testerebbe? Se non è troppo disturbo :)
Ho letto ovunque su quanto siano fantastici, ma per qualche motivo non riesco a capire esattamente come dovrei provare qualcosa. Qualcuno potrebbe forse pubblicare un pezzo di codice di esempio e come lo testerebbe? Se non è troppo disturbo :)
Risposte:
C'è un terzo "framework", che è di gran lunga più facile da imparare - anche più facile di Simple Test, si chiama phpt.
Un primer può essere trovato qui: http://qa.php.net/write-test.php
Modificare: ho appena visto la tua richiesta di codice di esempio.
Supponiamo che tu abbia la seguente funzione in un file chiamato lib.php :
<?php
function foo($bar)
{
return $bar;
}
?>
Veramente semplice e diretto, viene restituito il parametro che si passa. Quindi diamo un'occhiata a un test per questa funzione, chiameremo il file di test 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"
In poche parole, forniamo $bar
valore al parametro "Hello World"
e noi var_dump()
la risposta della chiamata di funzione afoo()
.
Per eseguire questo test, usa: pear run-test path/to/foo.phpt
Ciò richiede un'installazione funzionante di PEAR sul sistema, che è abbastanza comune nella maggior parte delle circostanze. Se è necessario installarlo, consiglio di installare l'ultima versione disponibile. Se hai bisogno di aiuto per configurarlo, non esitare a chiedere (ma fornisci il sistema operativo, ecc.).
run-tests
?
Sono disponibili due framework che è possibile utilizzare per i test di unità. Simpletest e PHPUnit , che preferisco. Leggi i tutorial su come scrivere ed eseguire test sulla homepage di PHPUnit. È abbastanza facile e ben descritto.
È possibile rendere i test di unità più efficaci modificando lo stile di codifica per adattarlo.
Consiglio di sfogliare il blog di Google Testing , in particolare il post sulla scrittura di codice testabile .
Ho tirato il mio perché non avevo tempo per imparare qualcun altro a fare le cose, ci sono voluti circa 20 minuti per scriverlo, 10 per adattarlo per la pubblicazione qui.
Unittesting è molto utile per me.
questo è un po 'lungo ma si spiega da solo e c'è un esempio in fondo.
/**
* 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();
Questo produce:
Test: TestOne è stato un fallimento / ** * Questo test è progettato per fallire ** / (righe: 149-152; file: /Users/kris/Desktop/Testable.php) Test: TestTwo è stato un successo
Ottieni PHPUnit. È molto facile da usare.
Quindi inizia con affermazioni molto semplici. Puoi fare molto con AssertEquals prima di entrare in qualsiasi altra cosa. È un buon modo per bagnarti i piedi.
Potresti anche provare a scrivere prima il tuo test (dato che hai dato alla tua domanda il tag TDD) e poi scrivere il tuo codice. Se non l'hai mai fatto prima, ti apre gli occhi.
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);
}
Per semplici test E documentazione, php-doctest è abbastanza carino ed è un modo davvero semplice per iniziare poiché non è necessario aprire un file separato. Immagina la funzione di seguito:
/**
* Sums 2 numbers
* <code>
* //doctest: add
* echo add(5,2);
* //expects:
* 7
* </code>
*/
function add($a,$b){
return $a + $b;
}
Se ora esegui questo file tramite phpdt (runner da riga di comando di php-doctest) verrà eseguito 1 test. Il doctest è contenuto all'interno del blocco <code>. Doctest è nato in python e va bene per fornire esempi utili ed eseguibili su come dovrebbe funzionare il codice. Non puoi usarlo esclusivamente perché il codice stesso sarebbe disseminato di casi di test, ma ho scoperto che è utile insieme a una libreria tdd più formale: io uso phpunit.
Questa prima risposta qui lo riassume bene (non è unità vs doctest).
I test di codeception sono molto simili ai test unitari comuni, ma sono molto potenti nelle cose in cui è necessario prendere in giro e stubbing.
Ecco il test del controller di esempio. Notare con quanta facilità vengono creati gli stub. Con quale facilità si controlla che il metodo sia stato invocato.
<?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');
}
}
Inoltre ci sono altre cose interessanti. Puoi testare lo stato del database, il filesystem, ecc.
Oltre agli ottimi suggerimenti sui framework di test già forniti, stai costruendo la tua applicazione con uno dei framework web PHP che ha il testing automatizzato integrato, come Symfony o CakePHP ? A volte avere un posto dove abbandonare i metodi di test riduce l'attrito di avvio che alcune persone associano ai test automatizzati e al TDD.
Troppo per ripubblicare qui, ma ecco un ottimo articolo sull'uso di phpt . Copre una serie di aspetti su phpt che sono spesso trascurati, quindi potrebbe valere la pena leggerlo per espandere la tua conoscenza di php oltre la semplice scrittura di un test. Fortunatamente l'articolo discute anche dei test di scrittura!
I principali punti di discussione
So che ci sono già molte informazioni qui, ma poiché questo viene ancora visualizzato nelle ricerche su Google, potrei anche aggiungere Chinook Test Suite all'elenco. È un framework di test semplice e piccolo.
Puoi facilmente testare le tue classi con esso e anche creare oggetti fittizi. Esegui i test tramite un browser web e (non ancora) tramite una console. Nel browser è possibile specificare quale classe di test o anche quale metodo di test eseguire. Oppure puoi semplicemente eseguire tutti i test.
Uno screenshot dalla pagina GitHub:
Quello che mi piace è il modo in cui affermi i test. Questo viene fatto con le cosiddette "affermazioni fluenti". Esempio:
$this->Assert($datetime)->Should()->BeAfter($someDatetime);
E anche la creazione di oggetti fittizi è un gioco da ragazzi (con una sintassi fluente):
$mock = new CFMock::Create(new DummyClass());
$mock->ACallTo('SomeMethod')->Returns('some value');
Ad ogni modo, ulteriori informazioni possono essere trovate sulla pagina github con un esempio di codice: