对于组成另一个对象作为其实现的一部分的对象,编写单元测试的最佳方法是什么,只有主要对象才会被测试?琐碎的例子:
class myObj { public function doSomethingWhichIsLogged() { // ... $logger = new logger('/tmp/log.txt'); $logger->info('some message'); // ... } }
我知道可以设计对象以便可以注入logger对象依赖项并因此在单元测试中进行模拟,但情况并非总是如此 - 在更复杂的场景中,您需要组合其他对象或调用静态方法.
由于我们不想测试logger对象,只有myObj,我们如何进行?我们用测试脚本创建一个stubbed"double"吗?就像是:
class logger { public function __construct($filepath) {} public function info($message) {} } class TestMyObj extends PHPUnit_Framework_TestCase { // ... }
这对于小对象来说似乎是可行的,但对于更复杂的API而言,这将是一个痛苦,其中SUT依赖于返回值.另外,如果你想测试对依赖项对象的调用,你可以使用模拟对象吗?有没有办法模拟由SUT实例化的对象而不是传入?
我已经阅读了关于模拟的手册页,但它似乎并没有涵盖这种依赖是由组合而不是聚合的情况.你怎么做呢?
跟随troelskn建议这是你应该做的一个基本的例子.
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 } }
有关模拟对象的更多信息,请参见手册.