此命令处理程序可测试吗?

时间:2018-12-07 16:26:21

标签: php database unit-testing session fixtures

我在理解单元测试方面取得了一些进步,但是我尝试测试的每种方法都有新的问题:

public function handle( SendNotification $command ) {

    $DTO = $this->Assembler->build();

    $subject = sprintf(
        $command->getFirstName(),
        $command->getLastName()
    );

    $EmailComponents = new EmailComponents(
        $subject,
        $DTO->getProject()->getSettings()->getValueOf( 'recipient' ),
        $this->OptionQuery->getOption( 'business_email' ),
        $this->NotificationRenderFactory->render( 'template' ) )
    );

    $this->Mailer->send( $EmailComponents );

}

$DTO基本上是一个聚合群集,其中“项目”为聚合根。它从PHP会话读取数据以确定当前项目,而OptionQuery从数据库读取数据。因此,我目前的理解是,我将必须创建一个固定装置,以设置一个聚集,一个测试数据库和一个为会话对象返回内容的模拟程序。那是正确的吗?如果是的话,为什么我要花那么多精力来测试一种方法?

编辑同时,我重构了handle方法以使其更具可测试性:

public function handle( SendNotification $command ) {

    $EmailComponents = $this->EmailComponentsAssembler->build( $command );

    $this->Mailer->setup( $EmailComponents );

    $this->Mailer->send();

}

尽管,汇编器的build方法(实际上更像是工厂)仍然很丑陋:

public function build( SendNotification $command ): EmailComponentsDTO {

    $request = Request::createFromGlobals();

    $Recipient = $this->ProjectRecipientEmailQuery->execute( $request->request->get( 'destination' ) );

    if ( !\is_email( $Recipient ) ) :

        throw new \Exception( 'No email address found!' );

    endif;

    return new EmailComponentsDTO(
        TRUE,
        $Recipient,
        (array)$command->getCustomField( 'additional_recipients' ),
        $this->OptionQuery->getOption( 'email_from' ),
        $this->OptionQuery->getOption( 'email_email' ),
        (string)$this->NotificationSubject->render( $command ),
        (string)$this->NotificationRenderFactory->render( 'EmailNotification', $command ),
        $command->getPriority()
    );
}

但是我觉得现在的担忧要好一些了。

1 个答案:

答案 0 :(得分:1)

单元测试用于在隔离的代码中查找错误。但是您的代码受交互作用的支配,例如与AssemblerMailerEmailComponents等的交互作用。此部分代码中的错误将在于与其他软件部分的交互作用:您以正确的顺序以正确的顺序调用正确的函数,并以正确的顺序调用具有正确种类的内容的参数?使用模拟进行测试不会回答您以下问题:如果您误解了如何调用库,则实现的模拟将反映您自己的误解。相反,您应该使用集成测试来测试此类代码。

其中唯一的算法代码是:

$subject = sprintf(
    $command->getFirstName(),
    $command->getLastName()
);

这甚至对我来说似乎是错误的,因为我希望某些格式字符串成为sprintf的第一个参数(但是我对php并不熟悉)。如果我对这是一个错误的说法是正确的,严格来说,它也是一个集成错误,但是您可以通过单元测试找到它,因为您无需打扰sprintf

因此,针对您的测试问题的一种可能的解决方案是将主题字符串的创建提取到一个小的辅助函数中,并通过单元测试来测试该字符串,而通过集成测试来测试其余功能。