我们有一些传统的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;
    }
}

我们最近的项目使用了基础laravel类的依赖注入,facades代表了hinted at by Taylor Otwell himself个.(我们对每个类使用构造函数注入,但为了保持示例简短,这里我使用方法注入并使用单个类.)

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;
    }
}

我知道facades can be mocked

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

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

这很适合unit tests人.我试图理解的问题是,这些外表是否被"全球"嘲笑.

例如,假设我正在编写一个集成测试(在模拟服务的同时测试几个相互连接的类,而不是使用实时服务的端到端测试),该测试在某个时候执行两个单独的类which contain the same facade that calls the same method with the same parameters.

在这些被调用的类之间,有一些复杂的功能,可以使用相同的参数更改facades方法返回的数据*

$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

我们的现代类很容易测试,因为facade所代表的底层类被注入到每个类中(在本例中,每个方法).这意味着我可以创建两个单独的mock,并将它们注入每个类(方法)以模拟不同的结果.

$legacyClass->cacheFunctionOne();

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

$legacyClass->cacheFunctionTwo();

不过,在遗留系统中,模拟的facade应该是"全局"的,因此当facade在每个类中运行时,the exact same value is returned.

我这样想对吗?

*I understand this example may seem completely redundant from a code architecture and testing point of view, but I am stripping out all real functionality to try and give some sort of 'simple' example of what I am asking.

推荐答案

Dependency Injection vs Facades

依赖项注入的主要好处之一是,一旦开始将依赖项注入到方法中,而不是在方法中实例化/硬编码依赖项,代码就变得更加可测试.这是因为您可以从内部单元测试传入依赖项,它们将通过代码传播.

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

依赖注入与外观形成鲜明对比.Facades是静态全局类,PHP语言不允许覆盖或替换静态类上的静态函数.Laravel立面使用模拟来提供模拟功能,它们受到上述相同事实的限制.

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

为了使用所需的不同数据集测试代码,最佳实践是重构遗留代码以使用DI.

Integration Tests

Easier method

另一种方法是在集成测试开始时调用多个Facade::shouldReceive(),并带有预期.确保您在集成测试中将要进行的每一次调用都有正确的预期数量和正确的顺序.考虑到现有的代码库,这可能是编写测试的更快方法.

Harder method

而依赖注入是编程的最佳实践.很可能您的代码库有太多遗留类,以至于重构需要花费难以置信的时间.在这种情况下,考虑使用带有fixture 的测试数据库进行端到端集成测试可能是值得的.

Appendix:

Laravel相关问答推荐

运行NPM Prod时出现VUE问题

拉威尔望远镜没有显示请求细节

使用Laravel判断其他数据库中是否存在记录

在Laravel中URL中添加保护ID

在 laravel 中查询此类数据的最佳做法是什么?

Laravel API 版本控制文件夹 struct

Laravel 附加额外字段

允许的内存大小 134217728 字节用尽(试图分配 20480 字节) Laravel

Laravel 5 - ErrorException 无法打开流:权限被拒绝

laravel 中的 Http Post 使用 fetch api 给出 TokenMismatchException

从 sqlite DB 登录 Laravel,得到PDOException 找不到驱动程序

在 Laravel 中下载后如何重定向?

如何卸载 Laravel?

使用模型工厂、一对一和一对多关系定义 Laravel 外键,而不创建不必要的模型

Laravel Eloquent 查询生成器默认 Where 条件

在 Laravel 中结合 AND/OR Eloquent的查询

Laravel 用户能力

Laravel 4 - 一个表单中有两个提交按钮,并且两个提交都由不同的操作处理

开发中的 Laravel 和视图缓存 - 无法立即看到更改

Laravel 如何在 Eloquent 模型中添加自定义函数?