В настоящее время я пытаюсь протестировать конструктор класса в модульном тесте.
Я не уверен, является ли экземпляр этого объекта «божественным объектом», я бы сказал, что это не так, поскольку он объединяет только несколько других компонентов.
В любом случае, я открыт для лучшего дизайна.
Таким образом, грубая диаграмма классов выглядит следующим образом
World
это подозреваемый класс бога. Его зависимости, ServiceProvider
, CommandRegistry
, EventHub
а также Environment
вводятся через его конструктор.
В своем конструкторе World
делает следующие вещи:
($this, 'onCommandIssued')
с eventHub
таким образом world
получает уведомления обо всех командах, выполняемых не через world
сам экземпляр (world
имея также метод executeCommand
)$this->environment->adoptWorld($this)
, Роль среды состоит в том, чтобы адаптировать мир к некоторым реалиям работающей среды, например, веб-среда имеет некоторые специфические сервисы, которые недоступны в среде консольного приложения (например, сервис «сеанс»)$this->eventHub->notify(new WorldConstructedEvent($this));
Может быть, это выглядит как тяжелый конструктор, но это просто то, что определяется как «конструирование мира».
World
в основном шлюз для отправки команд (как объекты передачи данных, через World::executeCommand()
) к которым разные сервисы могут регистрировать хуки.
Теперь к проблемам / вопросам:
@uses
аннотации, что делает его похожим на что-либо еще, кроме модульного теста. Что это, функциональный тест? Unit-тестирование 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);
}}
Других решений пока нет …