donglin4636 2015-12-03 10:37
浏览 58
已采纳

使用Laravel / Lumen外观进行测试

So, I'm trying to write a unit test for a piece of code that uses a facade, and it looks like this:

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

and in the unit test, I am trying to mock the facade call:

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

The only problem with this is that when Laravel tries to make an instance of underling class for the MYFACADE, it will of course run the constructor, and right there, is hardcoded database connection call. So without changing my facade, and removing database connection from the constructor of the facade class, is there any way to mock facade call without running the constructor of facade ?

UPDATE: Rest of the setup:

Facade 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');

Service provider:

class MyServiceProvider extends ServiceProvider
{

    public function register()
    {

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

The underlying class used by facade

class myClasThatDoesSomething
{
   public function __construct()  
    {
       // opens db connection here  
    }

    public function doSomething()
    {

    }
}

example class that uses facade:

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

Unit test that checks if the testMethod() returns 'testValue';

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

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

1条回答 默认 最新

  • dqcd84732 2015-12-03 10:54
    关注

    First of all Laravel documentation reads:

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

    Please make sure your MYFACADE follows this pattern. There is no room for constructor, databases, etc. If you test MYFACADE::doSomething() you should mock all other classes that are being used by the function.

    Secondly, the following piece of code

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

    mocks the facade itself to test something else that uses MYFACADE::doSomething(). It should return an instance of Mockery which returns false wherever MYFACADE::doSomething() is being called within the test.

    EDIT:

    Laravel's Facade mock implementation instantiates the underlaying class, which allow to test services with thin constructors. Although it is the best practice, it may not always be possible to do. Assuming you cannot move DB connection logic from the constructor, your best bet will be to mock the service manually:

    public function testTestMethod()
    {
        MYFACADE::swap(\Mockery::mock()
            ->shouldReceive('doSomething')
            ->once()
            ->andReturn('testValue')
            ->getMock()
        );
    
        $instance = new \App\TestClass();
        $value = $instance->testMethod();
        $this->assertEquals('testValue', $value);
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 基于卷积神经网络的声纹识别
  • ¥15 Python中的request,如何使用ssr节点,通过代理requests网页。本人在泰国,需要用大陆ip才能玩网页游戏,合法合规。
  • ¥100 为什么这个恒流源电路不能恒流?
  • ¥15 有偿求跨组件数据流路径图
  • ¥15 写一个方法checkPerson,入参实体类Person,出参布尔值
  • ¥15 我想咨询一下路面纹理三维点云数据处理的一些问题,上传的坐标文件里是怎么对无序点进行编号的,以及xy坐标在处理的时候是进行整体模型分片处理的吗
  • ¥15 CSAPPattacklab
  • ¥15 一直显示正在等待HID—ISP
  • ¥15 Python turtle 画图
  • ¥15 stm32开发clion时遇到的编译问题