集成测试:模拟门面 vs 注入模拟

15
我们有一些使用facade的类的旧版laravel项目。
use Cache;

LegacyClass
{
    public function cacheFunctionOne()
    {
         $result = Cache::someFunction('parameter');

         // logic to manipulate result

         return $result;
    }

    public function cacheFunctionTwo()
    {
         $result = Cache::someFunction('parameter');

         // different logic to manipulate result

         return $result;
    }
}

我们最近的项目使用依赖注入来代替facade所表示的基础laravel类,正如Taylor Otwell本人所暗示的。 (我们对于每个类使用构造函数注入,但为了简化示例,这里我使用了方法注入并使用了一个单一的类。)
use Illuminate\Cache\Repository as Cache;

ModernClass
{
    public function cacheFunctionOne(Cache $cache)
    {
         $result = $cache->someFunction('parameter');

         // logic to manipulate result

         return $result;
    }

    public function cacheFunctionTwo(Cache $cache)
    {
         $result = $cache->someFunction('parameter');

         // different logic to manipulate result

         return $result;
    }
}

我知道 门面可以被模拟

public function testExample()
{
    Cache::shouldReceive('get')
                ->once()
                ->with('key')
                ->andReturn('value');

    $this->visit('/users')->see('value');
}

这对于单元测试非常有效。我试图理解的问题是,如果这些外观被“全局”模拟了。
例如,假设我正在编写一个集成测试(测试一些相互关联的类,同时模拟服务 - 不是使用实时服务的端到端测试),在某个时刻执行两个不同的类,这两个类包含调用相同方法且参数相同的相同外观。在这些类被调用之间,会执行一些复杂的功能,使用相同的参数更改由该外观方法返回的数据。
$modernClass->cacheFunctionOne($cache); // easily mocked

// logic that changes data returned by laravel Cache object function 'someFunction'

$modernClass->cacheFunctionTwo($cache); // easily mocked with a different mock

我们现代化的类很容易进行测试,因为外观所代表的基础类被注入到每个类中(在这个例子中是每个方法)。这意味着我可以创建两个不同的模拟对象并将它们注入到每个类(方法)中以模拟不同的结果。
$legacyClass->cacheFunctionOne();

// logic that changes data returned by laravel Cache object function 'someFunction'

$legacyClass->cacheFunctionTwo();

在传统系统中,似乎模拟的外观是“全局”的,因此当在每个类中运行外观时,返回的确切值相同
我想知道我的想法是否正确?
*我理解这个例子从代码架构和测试角度看似乎完全多余,但我剥离了所有真实功能,试图给出一些所问问题的“简单”示例。
1个回答

7

依赖注入 vs 门面模式

依赖注入的主要优点之一是,一旦您开始将依赖注入到方法中而不是在方法内实例化 / 硬编码它们,代码就变得更加易于测试。这是因为您可以从单元测试中传递依赖项,然后它们会通过代码进行传播。

参见:http://slashnode.com/dependency-injection/

依赖注入与门面模式形成鲜明对比。门面是静态全局类,PHP语言不允许覆盖或替换静态类上的静态函数。 Laravel 门面使用 Mockery 提供模拟功能,并受到与上述相同的限制。

集成测试可能会出现问题,如果您希望从非模拟缓存中检索数据,但一旦使用 Facade::shouldReceive() ,那么 Facade::get() 将被模拟缓存覆盖。反之亦然。因此,在交错调用模拟和未模拟数据的情况下,门面是不合适的。

为了使用所需的不同数据集测试您的代码,最佳做法是重构您的遗留代码以使用 DI。

集成测试

更容易的方法

另一种方法是在集成测试开始时调用多个 Facade::shouldReceive() ,对于您将在集成测试中进行的每个调用,确保您具有正确顺序的正确数量的期望。鉴于您现有的代码库,这可能是编写测试更快的方法。

更难的方法

虽然依赖注入是编程最佳实践。但您的代码库可能有许多旧类,重构它们可能需要不可思议的时间。在这种情况下,考虑使用具有固定装置的测试数据库进行端到端集成测试可能是值得的。

附录:


感谢您的详细解释。 - myol

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接