如何在无法注入依赖项的地方对测试方法进行单元测试


How to unit test method where you cannot inject dependency

我有以下代码:

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 关键字。它要么是一个私有细节 - 那么你不需要对它进行单元测试 - 要么不是,那么你应该把它作为一个依赖项,或者如果它是公共的,它甚至应该是公共接口的一部分。