Test oggetti con dipendenze in PHPUnit
-
23-08-2019 - |
Domanda
Per gli oggetti che compongono un altro oggetto, come parte della loro attuazione, qual è il modo migliore per scrivere il test di unità in modo che solo l'oggetto principio viene testato? Esempio banale:
class myObj {
public function doSomethingWhichIsLogged()
{
// ...
$logger = new logger('/tmp/log.txt');
$logger->info('some message');
// ...
}
}
So che l'oggetto potrebbe essere progettato in modo tale che la dipendenza oggetto logger potrebbe essere iniettato e, quindi, preso in giro in un test di unità, ma non è sempre il caso - in scenari più complessi, si ha bisogno di comporre altri oggetti o effettuare chiamate a metodi statici.
Come noi non vogliamo testare l'oggetto logger, solo il myObj, come procediamo? Si fa a creare un contuso "doppio" con lo script di test? Qualcosa di simile:
class logger
{
public function __construct($filepath) {}
public function info($message) {}
}
class TestMyObj extends PHPUnit_Framework_TestCase
{
// ...
}
Questo sembra fattibile per piccoli oggetti, ma sarebbe un dolore per le API più complesse dove il SUT dipendeva dai valori di ritorno. Inoltre, cosa succede se si desidera verificare le chiamate all'oggetto di dipendenza nello stesso stato è possibile con oggetti fittizi? C'è un modo di beffardo oggetti che sono istanziati dal SUT anziché essere passato nel?
Ho letto la pagina man su schernisce, ma non sembra per coprire questa situazione in cui la dipendenza è composto piuttosto che aggregata. Come si fa a farlo?
Soluzione
Come ti sembra di essere a conoscenza già, Calcestruzzo Classe Dipendenze rende il test difficile (o addirittura impossibile). È necessario disaccoppiare tale dipendenza. Un cambiamento semplice, che non si rompe l'API esistente, è per difetto al comportamento corrente, ma fornire un gancio per scavalcarlo. Ci sono un certo numero di modi in cui questo potrebbe essere attuato.
Alcune lingue dispongono di strumenti in grado di iniettare classi finte in codice, ma non so nulla di simile per PHP. Nella maggior parte dei casi, si sarebbe probabilmente essere meglio refactoring il codice in ogni caso.
Altri suggerimenti
Dopo troelskn consigliare ecco un esempio di base di ciò che si dovrebbe fare.
<?php
class MyObj
{
/**
* @var LoggerInterface
*/
protected $_logger;
public function doSomethingWhichIsLogged()
{
// ...
$this->getLogger()->info('some message');
// ...
}
public function setLogger(LoggerInterface $logger)
{
$this->_logger = $logger;
}
public function getLogger()
{
return $this->_logger;
}
}
class MyObjText extends PHPUnit_Framework_TestCase
{
/**
* @var MyObj
*/
protected $_myObj;
public function setUp()
{
$this->_myObj = new MyObj;
}
public function testDoSomethingWhichIsLogged()
{
$mockedMethods = array('info');
$mock = $this->getMock('LoggerInterface', $mockedMethods);
$mock->expects($this->any())
->method('info')
->will($this->returnValue(null));
$this->_myObj->setLogger($mock);
// do your testing
}
}
Maggiori informazioni su oggetti mock può essere trovato rel="noreferrer"> .
Sembra che ho capito male la domanda, vorrei provare di nuovo:
Si dovrebbe usare il pattern Singleton o una fabbrica per il logger, se non è già troppo tardi:
class LoggerStub extends Logger {
public function info() {}
}
Logger::setInstance(new LoggerStub());
...
$logger = Logger::getInstance();
Se non è possibile modificare il codice, è possibile utilizzare un catch-all classe che sta sovraccaricando __ call ()
class GenericStub {
public function __call($functionName, $arguments) {}
}
Non è in realtà un ragionevolmente nuova estensione per la classe PHP sovraccarico rilasciato dagli stessi ragazzi che costruiscono PHPUnit. Esso consente di sostituire il nuovo operatore nei casi in cui non è possibile refactoring del codice, purtroppo non è così semplice da installare su Windows.
L'URL è http://github.com/johannes/php -test-aiutanti / blob / master /