我有以下代码:
class Plugin {
protected $manager;
public function activate(Composer $composer, IOInterface $io)
{
$this->manager = new Manager();
$this->doSomething($this->manager);
}
private function doSomething(Managaer $manager)
{
$manager->add('first', function() {
//do something.
});
$manager->add('second', function() {
//do something.
});
}
}
如何断言add
方法是在Manager
类上调用的?
Plugin
类是为我实例化的,不能注入依赖项。激活方法也是为我调用的(不在我的控制范围内(,所以我不能传入依赖项。
我通常通过使用以下额外方法并模拟它以返回模拟的Manager
实例来测试这一点
class Plugin {
protected $manager;
public function activate(Composer $composer, IOInterface $io)
{
$this->doSomething($this->getManager());
}
private function doSomething(Manager $manager)
{
$manager->add('first', function() {
//do something.
});
$manager->add('second', function() {
//do something.
});
}
public function getManager()
{
return new Manager;
}
}
这看起来像下面这样:
//get a mock manager
$manager = $this->getMock('Manager');
//assert that method should be called
$manager->expects($this->once())
->method('add')
->with($this->isInstanceOf('Closure'));
//create mock plugin but only mock getManager method
$plugin = $this->getMock('Plugin');
$plugin->expects($this->once())
->method('getManager')
->will($this->returnValue($manager));
$plugin->activate(/** args **/);
这对我来说,感觉很笨拙。我觉得我不应该嘲笑Plugin
班。其他人如何解决这个问题?
你有管理器的二传手和getter吗? 如果未设置类变量,则返回默认实例。这样,在测试中,我可以使用模拟实例调用setManager()
方法?
这听起来也不是很好,因为我正在编写额外的代码来对类进行单元测试!
如果您不想直接修改正在测试的Plugin
类,只需创建某种方式将经理模拟注入其中:
class PluginTester extends Plugin
{
public function setManager(Plugin $plugin, Manager $manager) {
$plugin->manager = $manager;
}
}
然后,您可以使用它来注入模拟:
$plugin = new Plugin();
$stubManager = new ManagerMock();
$util = new PluginTester();
$util->setManager($plugin, $stubManager);
或者,如果您确实需要模拟这些对象,请不要在内部或您的类中使用 new
关键字。它要么是一个私有细节 - 那么你不需要对它进行单元测试 - 要么不是,那么你应该把它作为一个依赖项,或者如果它是公共的,它甚至应该是公共接口的一部分。