phpspec 单元测试 - 使用 ioc / 服务注册表来交付要测试的具体 class

phpspec unit testing - using ioc / service registry for delivering the concrete class to test

我是测试新手,我不确定我的做法是否正确:

我不想对特定的 class 进行单元测试,而是对从我的 ioc 容器中解决的任何 class 进行单元测试。在 ioc 容器中,我将接口绑定到具体的 classes,如下所示:

示例(我正在使用 Laravel 5):

// in a service provider
public function register(){ 

    $this->app->bind('FooInterface', function() { 
        return new SomeConcreteFoo; 
    }); 
 }

然后我想针对 FooInterface 而不是 SomeConcreteFoo 编写单元测试,后者可以在稍后与其他 class 交换。

我想这样做的原因是,在我看来,相关测试应该针对我的任何 ioc 容器 returns,因为这就是我将在应用程序中使用的内容。在我看来,测试应该在界面级别完成,因为这是我定义应用程序其余部分对我的 class 的期望的地方。

我很难找到有关如何执行此操作的任何信息,这表明我可能以错误的方式思考这个问题。例如,也许我想要完成的更像是集成测试而不是单元测试。

所以第一个问题是:我是否正在考虑以正确的方式进行测试?如果我不是 - 您对替代测试路径的最佳实践有什么建议吗?

第二个问题:如果我的想法是正确的 - 我如何设置 phpspec 以使用 Laravel IOC 容器,以便我可以针对任何 IOC returns..

I want to not to do a unit test on a specific class but on whatever class get resolved out of my ioc container. In the ioc container I bind my interfaces to concrete classes, like so [...]

单元测试不是这样写的。单元测试是关于孤立地描述 class 行为,因此实际创建的唯一真实对象是被测 class(有时是简单的值对象)。

Then I want to write unit test against FooInterface and not SomeConcreteFoo which could be swapped out with some other class at a later point.

这确实是您应该编写单元测试的方式。更喜欢协作者的界面。

每个模拟框架都支持此功能,并且会为您创建测试替身,而不会强制您提供特定的实现。

class BarSpec extends ObjectBehavior
{
    function it_does_amazing_things(FooInterface $foo)
    {
        $results = ['a', 'b', 'c'];

        $foo->find('something')->willReturn($results);

        $this->findMeSometing()->shouldReturn($results);
    }
}

在此特定示例中,PhpSpec 将使用 Prophecy(其模拟框架)创建 FooInterface 的测试替身并将其注入示例方法。您对该对象的处理决定了它是假的、存根还是模拟的。

The reason I want to do this is that it seems to me that the relevant testing should target whatever my ioc container returns, since that is what I'll be using in the application.

如上所述,单元测试关注单个 class 的行为。它的合作者通常是伪造的。这是出于几个原因。其中之一是速度。另一个是反馈。如果测试失败,我们将得到关于 class 损坏的明确反馈。如果您正在创建所有协作者,而不是使用测试替身,那么一个错误可能会使您的整个测试套件变红。我什至不会提到维护和创建所有需要的对象会有多困难(尽管容器在这里可以提供帮助)。

请记住,编写单元测试更像是一种设计 activity,而不是测试 activity。

For instance maybe what I'm trying to accomplish is more like an integration test rather than a unit test.

确实如此。阅读有关测试金字塔的更多信息。大多数测试应该是单元测试。然后,您应该进行一定数量的集成和验收测试,这将同时执行多个 class。你想要比集成测试更多的单元测试的原因是后者更脆弱并且更难maintain/change它们。

使用 PHPUnit 进行集成测试。 PhpSpec 不是这项工作的正确工具。 PhpSpec 非常适合设计您的 classes(编写单元测试),特别是如果您先进行测试。

The second question: In case my thinking is sound - how do I setup phpspec to make use of the Laravel IOC container, so that I can test against whatever the IOC returns..

你不知道。不过,您可以考虑在集成测试中使用容器。

一些阅读: