Reputation: 1475
I have the following code:
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.
});
}
}
How can I assert that the add
method was called on the Manager
class?
The Plugin
class is instantiated for me and cannot have dependencies injected. The activate method is also called for me (not in my control), so I cannot pass in dependencies.
I usually test this by having the following extra method and mocking it to return a mocked instance of 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;
}
}
And that would look like the following:
//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 **/);
This to me, feel hacky. I don't feel like I should be mocking the Plugin
class. How do other people tackle this issue?
Do you have a setter & a getter for the Manager? where if a class variable is not set, return a default instance. This way in the tests I can call the setManager()
method with a mocked instance?
That also doesn't sound great because I'm writing extra code just to unit test the class!
Upvotes: 1
Views: 592
Reputation: 197659
If you don't want to modify the Plugin
class you have under test directly, just create yourself some way to inject your manager mock into it:
class PluginTester extends Plugin
{
public function setManager(Plugin $plugin, Manager $manager) {
$plugin->manager = $manager;
}
}
You can then use it to inject the mock:
$plugin = new Plugin();
$stubManager = new ManagerMock();
$util = new PluginTester();
$util->setManager($plugin, $stubManager);
Alternatively do not use the new
keyword inside or your class if you really need to mock those objects. It's either a private detail - then you don't need to unit test it - or it's not, then you should have it as an dependency or it should be even part of the public interface if it's public.
Upvotes: 2