我目前要做的是在单元测试中测试类的构造函数。
我不确定这个对象的实例是否是"上帝对象",我会说它不是,因为它只聚合了其他几个组件。
无论哪种方式,我都愿意接受更好的设计。粗糙的类图看起来像这样
World
是可疑的上帝阶级。其依赖项ServiceProvider
,CommandRegistry
,EventHub
和Environment
通过其构造函数注入。
在其构造函数中,World
执行以下操作:
($this, 'onCommandIssued')
注册一个钩子eventHub
,以便world
收到有关所有正在执行的命令的通知,而不是通过world
实例本身(world
也是方法executeCommand
)$this->environment->adoptWorld($this)
。环境的作用是使世界适应运行环境的某些现实,例如,Web环境具有一些在控制台应用程序环境中不可用的特定服务(例如"会话&#34 ;服务)$this->eventHub->notify(new WorldConstructedEvent($this));
也许这看起来像一个沉重的构造函数,但它只是被定义为"构建世界"。
World
基本上是发送命令的网关(作为数据传输对象,通过World::executeCommand()
),不同的服务可以注册钩子。
现在问题/问题:
@uses
注释,这使得除了单元测试之外还感觉不到其他任何东西。什么是功能测试?单元测试World
很笨拙,测试其他任何东西都是微不足道的,我不会在任何其他测试中看到这个问题,这让我问自己为什么会这样,以及如何改进设计。World
是神的对象吗?它所做的就是聚合其他组件并将呼叫转发给它们。World
的方法进行单元测试?如果我使用大量的存根并依赖注入它们,它仍然是单元测试吗?这是针对域驱动的设计(复杂)应用程序,我愿意接受可以使设计更好(可测试和解耦)的建议。
如果您需要更多详细信息,请在评论中告诉我。
由于我不知道这次讨论将会导致什么,我可以改进我的问题。
答案 0 :(得分:0)
我终于通过设置适当的期望并模拟依赖项来对该类进行单元测试:
<?php
namespace Common\World;
use TestFramework\TestCase;
class WorldTest extends TestCase
{
/**
* @test
* @covers \Common\World\World::__construct
* @uses \Common\World\World::setEventHub
* @uses \Common\World\Event\Adopted
*/
public function construction()
{
/** @var \Common\World\Environment $environmentStub |\PHPUnit_Framework_MockObject_MockObject */
$environmentStub = $this->getMockBuilder('Common\World\Environment')->getMock();
/** @var \Common\World\EventHub|\PHPUnit_Framework_MockObject_MockObject $eventHubStub */
$eventHubStub = $this->getMock('Common\World\EventHub');
$environmentStub->expects($this->once())
->method('adoptWorld')
->will($this->returnCallback(function (World $world) use ($eventHubStub) {
$world->setEventHub($eventHubStub);
return true;
}));
$eventHubStub->expects($this->once())
->method('trigger')
->with($this->isInstanceOf('Common\World\Event\Adopted'));
$this->assertInstanceOf('Common\World\World', new World($environmentStub));
}
/**
* @test
* @covers \Common\World\World::__construct
* @expectedException \RuntimeException
* @expectedExceptionMessage the environment has rejected this world for incompatibility reasons
*/
public function construction_rejected()
{
/** @var \Common\World\Environment $environmentStub */
$environmentStub = $this->getMockBuilder('Common\World\Environment')->getMock();
$environmentStub->expects($this->once())
->method('adoptWorld')
->will($this->returnValue(false));
new World($environmentStub);
}
}