我该如何模拟工匠命令中使用的类进行测试?

时间:2018-10-09 14:47:27

标签: php laravel phpunit mockery

我有一个工匠命令,它调用另一个类函数。此功能会向其他服务器发送获取请求,我不希望在测试过程中发生此获取请求。

我通常的解决方案是使用嘲笑来模拟该函数,但这似乎不起作用。

为什么我使用Artisan::call('command::getFoo')调用工匠命令时没有调用我的模拟程序?

命令类

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Foo;

class GetFoo extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:getFoo';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Get the foo data';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {       
        return Foo::get();  // Returns true
    }
}

测试班

namespace Tests\Feature;

use Tests\TestCase;
use App\Foo;

class FooCommandTest extends TestCase 
{
    public function testThatWeCanGetFoo()
    {
        $fooClass = Mockery::mock(Foo::class);
        $fooClass->shouldReceive(['get' => false]); // Overwrite the foo class to return false instead of true
        $fooData = \Artisan::call('command:getFoo');

        $this->assertFalse($fooData);
    }
}

运行测试时,它失败了,因为它仍然恢复为真。这意味着没有调用嘲讽类。这里发生了什么?如何测试该命令?

1 个答案:

答案 0 :(得分:1)

您可以尝试创建Artisan Facade使用的根类的伪子对象,并覆盖执行不必要操作的任何方法。然后,在您的测试中,调用Artisan::swap($dummyObj);来替换它。

-

它没有充分的文档证明,我想在寻找一种允许某些事件在我的测试中触发并禁止其他事件避免附带行为的方法时遇到了这个问题。因此Illuminate\Support\Facades\Event::fake()是如何使用它的一个很好的例子。

所以偷看一下:

  • config/app.phpArtisan定义为指向Illuminate\Support\Facades\Artisan
  • Illuminate\Support\Facades\Artisan::getFacadeAccessor()将外观访问器的名称定义为Illuminate\Contracts\Console\Kernel接口名称。
  • 并且Kernel接口在bootstrap/app.php中绑定到单个App \ Console \ Kernel。

全部累加,这意味着您每次致电\Artisan时,实际上是在与App\Console\Kernel的可重用实例进行通话。

App\Console\Kernel扩展了Illuminate\Foundation\Console\Kernel,并且是拥有您想要控制其行为的call()命令的东西。

那么您想要的就是这样:

namespace Tests\Dummies;

use App\Console\Kernel as BaseKernel;

class Artisan extends BaseKernel
{
    // This will override the parent's call() and block it from doing anything.
    public function call($command, array $parameters = [], $outputBuffer = null)
    {
        // -- Do nothing instead, or add some debug logging here --
    }
}

然后,作为测试内或setUp()方法一部分的第一个操作,

$dummyArtisan = app(Tests\Dummies\Artisan::class);
Artisan::swap($dummyArtisan);

注意,所有这些未经测试。过去我做过类似的事情,可以肯定地说这个理论是可靠的。但是我不知道这个特定的代码是否是

-

您可能还想研究Illuminate\Support\Facades\Facade::spy()Illuminate\Support\Facades\Facade::shouldReceive()。我只是在看这里的代码来发现他们的答案时发现他们的,他们让我想知道我的建议是否会重塑轮子。看起来Facades的设计有点了解它们很难被模拟,因此他们有一些工具可以用来制作。