Php 关于god对象、测试类型、测试覆盖率以及如何使类单元可测试的说明
我目前试图做的是在单元测试中测试类的构造函数 我不确定这个对象的实例是否是“上帝对象”,我会说它不是,因为它只聚合了几个其他组件 不管怎样,我愿意接受更好的设计 粗略的类图如下所示Php 关于god对象、测试类型、测试覆盖率以及如何使类单元可测试的说明,php,unit-testing,testing,domain-driven-design,functional-testing,Php,Unit Testing,Testing,Domain Driven Design,Functional Testing,我目前试图做的是在单元测试中测试类的构造函数 我不确定这个对象的实例是否是“上帝对象”,我会说它不是,因为它只聚合了几个其他组件 不管怎样,我愿意接受更好的设计 粗略的类图如下所示 World是可疑的神类。它的依赖项,ServiceProvider、CommandRegistry、EventHub和Environment通过其构造函数注入 在其构造函数中,World执行以下操作: 将其依赖项存储在私有字段中 向eventHub注册一个hook($this,'onCommandIssued'),
World
是可疑的神类。它的依赖项,ServiceProvider
、CommandRegistry
、EventHub
和Environment
通过其构造函数注入
在其构造函数中,World
执行以下操作:
eventHub
注册一个hook($this,'onCommandIssued')
,以便world
接收有关所有命令的通知,这些命令不是通过world
实例本身执行的(world
还有一个方法executeCommand
)$this->environment->adoptWorld($this)
。环境的作用是使世界适应运行环境的某些实际情况,例如,web环境具有一些在控制台应用程序环境中不可用的特定服务(例如“会话”服务)$this->eventHub->notify(newworldconstructedevent($this))代码>
World
基本上是发送命令(作为数据传输对象,通过World::executeCommand()
)的网关,不同的服务可以向其注册钩子
现在谈谈问题:
@用法
注释,这让它感觉像是单元测试以外的任何东西。那是什么,功能测试?单元测试World
很尴尬,测试其他任何东西都是非常琐碎的,我没有看到这个问题出现在任何其他测试中,这让我问自己为什么会这样,以及如何改进设计World
是神的对象吗?它所做的只是聚合其他组件并将调用转发给它们World
的方法进行单元测试?如果我使用了很多存根,并且注入了依赖项,这仍然是单元测试吗由于我不知道这场讨论将导致什么,我可能会改进我的问题。我最终通过设置适当的期望值和模拟依赖项来对类进行单元测试:
<?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);
}
}
我认为您应该提供一个mock EventHub,并检查是否调用了hook方法。根据需要对其他依赖项重复此操作。我怀疑我在你的问题中遗漏了什么。你是说存根EventHub
,对吗?测试的类是World
,因此这将是我的示例中唯一的模拟。无论如何,谢谢,这是一个好主意。模拟和存根不一样:。为了测试您的构造函数,我们关心的只是使用预期参数调用EventHub和Environment方法。作为一名模拟测试人员(使用Fowler的术语),我会模拟依赖项。您可能更像是一个经典的测试人员。AFAIK mock是测试断言的对象,而存根是您注入到SUT中的“mock”,SUT依赖于它(“la依赖注入”)。