使用 Laravel/Lumen 门面进行测试

Testing with Laravel/Lumen facade

所以,我正在尝试为一段使用外观的代码编写单元测试,它看起来像这样:

public function test() {
  MYFACADE::doSomething();
}

并且在单元测试中,我试图模拟外观调用:

MYFACADE::shouldReceive('doSomething')
    ->once()
    ->andReturn(false);

唯一的问题是,当 Laravel 尝试为 MYFACADE 创建底层 class 的实例时,它当然会 运行 构造函数,就在那里,是硬编码的数据库连接调用。因此,在不更改外观并从外观 class 的构造函数中删除数据库连接的情况下,是否有任何方法可以在不 运行 连接外观构造函数的情况下模拟外观调用?

更新: 其余设置:

门面class

class MyFacade extends Facade 
{
    protected static function getFacadeAccessor() 
    { 
        return 'myfacade'; 
    }
}

app.php

$app->register(App\Providers\MyServiceProvider::class);
class_alias('App\Facades\MyFacade', 'MYFACADE');

服务提供商:

class MyServiceProvider extends ServiceProvider
{

    public function register()
    {

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

facade

使用的底层class
class myClasThatDoesSomething
{
   public function __construct()  
    {
       // opens db connection here  
    }

    public function doSomething()
    {

    }
}

使用外观的示例class:

class TestClass 
{
   public function testMethod()
   {
       $returnValue = MYFACADE::doSomething();
       return $returnValue;
   }
}

单元测试检查 testMethod() returns 'testValue';

MYFACADE::shouldReceive('doSomething')
            ->once()
            ->andReturn('testValue');   

$instance = new TestClass();
$value = $instance->testMethod();
$this->assertEquals('testValue', $value);

首先Laravel documentation读到:

Facades provide a "static" interface to classes that are available in the application's service container.

请确保您的 MYFACADE 遵循此模式。没有构造函数、数据库等的空间。如果你测试 MYFACADE::doSomething() 你应该模拟函数正在使用的所有其他 classes。

其次,下面这段代码

MYFACADE::shouldReceive('doSomething')
    ->once()
    ->andReturn(false);

模拟外观本身以测试 使用 MYFACADE::doSomething() 的其他东西。它应该 return Mockery 的实例,其中 returns false 在测试中调用 MYFACADE::doSomething() 的任何地方。

编辑:

Laravel 的 Facade 模拟实现实例化底层 class,它允许使用瘦构造函数测试服务。尽管这是最佳实践,但并非总是可行。假设您不能从构造函数中移动数据库连接逻辑,最好的办法是手动模拟服务:

public function testTestMethod()
{
    MYFACADE::swap(\Mockery::mock()
        ->shouldReceive('doSomething')
        ->once()
        ->andReturn('testValue')
        ->getMock()
    );

    $instance = new \App\TestClass();
    $value = $instance->testMethod();
    $this->assertEquals('testValue', $value);
}