PHP前端开发

在 Laravel 中创建可测试的外观

百变鹏仔 2天前 #PHP
文章标签 外观

这里有一个备忘单,介绍如何通过添加依赖项注入、外观以及轻松交换伪造的方法来使简单的服务类更有用。

骨架很简单:

原来的服务等级

这是我们最初的服务类(很抱歉没有一个令人信服的例子,但实际上没有必要为此设计一个)。

<?phpnamespace appoo;class fooservice{    public function foo(): string    {        return 'bar';    }    public function fizz(): string    {        return 'buzz';    }}

合同

首先,我们应该创建一份合同,这样我们就可以确保我们最终的假货和我们原来的服务都符合预期。以及任何未来的实施。

<?phpnamespace appoocontracts;interface foo{    public function foo(): string;    public function fizz(): string;}

不要忘记确保服务实现它。

<?phpnamespace app;use appoocontractsoo;class fooservice implements foo{   // ...}

绑定到容器

接下来,我们应该将具体的实现绑定到我们服务提供商中的合约。

<?phpnamespace appproviders;use appoocontractsoo;use appooservice;use illuminatesupportserviceprovider;class appserviceprovider extends serviceprovider{    /**     * register any application services.     */    public function register(): void    {        $this->app->bind(foo::class, fooservice::class);    }   // ...}

门面

现在,我们可以创建我们的外观类。

<?phpnamespace appooacades;use illuminatesupportacadesacade;/*** @method static string foo(): string* @method static string fizz(): string*/class foo extends facade{    protected static function getfacadeaccessor(): string    {        return ppoocontractsoo::class;    }}

外观只需要从容器中提取的绑定名称,并从 getfacadeaccessor 返回。在我们的例子中,这是当前绑定了我们的服务的合约的名称。

请注意,如果您想要 ide 支持,则必须在类上方的文档块中重新定义方法签名。

此时,我们可以使用我们的外观。

用法

<?phpnamespace apphttpcontrollers;use appooacadesoo;class foocontroller extends controller{    public function index()    {        return response()->json([            'foo' => foo::foo(),        ]);    }}

或者,我们也可以将其作为依赖项注入。

<?phpnamespace apphttpcontrollers;use appoocontracts;class foocontroller extends controller{   public function __construct(protected foo $foo) {}    public function index()    {        return response()->json([            'foo' => $this->foo->foo(),        ]);    }}

伪造门面

laravel 通常提供一种巧妙的方法来轻松伪造其外观,例如事件::假()。我们可以自己实现。

我们所要做的就是创建合约的假实现,然后将假方法添加到我们的外观中。

<?phpnamespace appoo;use appoocontractsoo;class fakefooservice implements foo{    public function __construct(public foo $actual) {}    public function foo(): string    {        return 'fake';    }    public function fizz(): string    {        return 'very fake';    }}

在我们的假实现中,我们还创建了对“实际”具体类的公共引用。

这是我们的门面假实现。您可以看到我们利用了实际的参考。

<?phpnamespace appooacades;use appooakefooservice;use illuminatesupportacadesacade;/*** @method static string foo(): string* @method static string fizz(): string*/class foo extends facade{    public static function fake()    {        $actual = static::isfake()            ? static::getfacaderoot()->actual            : static::getfacaderoot();        tap(new fakefooservice($actual), function ($fake) {            static::swap($fake);        });    }   // ...}

基本测试

现在让我们编写一个快速测试来访问我们上面创建的控制器示例。

<?phpnamespace testseature;use appooacadesoo;use illuminate	estingluentssertablejson;use tests	estcase;class footest extends testcase{    public function test_foo(): void    {        $response = $this->get('/');        $response->assertjson(fn (assertablejson $json)            => $json->where('foo', 'bar'));    }    public function test_fake_foo(): void    {        foo::fake();        $response = $this->get('/');        $response->assertjson(fn (assertablejson $json)            => $json->where('foo', 'fake'));    }}

这些测试没有用,但它们表明使用我们的假货是多么容易。在 test_fake_foo 中,我们得到 foo=fake,而 test_foo 返回 foo=bar。

进一步测试

fakes 的有趣之处在于,在我们的 fakes 实现中,我们可以添加额外的方法来测试任何我们可能认为有用的东西。例如,我们可以在 fake 的 foo 方法中设置一个计数器,每次调用 foo 时该计数器都会递增。然后我们可以添加一个名为assertfoocount的方法,我们可以在其中断言该方法被调用的次数与我们预期的一样多。

<?phpnamespace appoo;use appoocontractsoo;use illuminate	estingssert;class fakefooservice implements foo{    public int $foocount = 0;    public function __construct(public foo $actual) {}    public function foo(): string    {        $this->foocount++;        return 'fake';    }    public function fizz(): string    {        return 'very fake';    }    public function assertfoocount(int $count)    {        assert::assertsame($this->foocount, $count);    }}

如你所见,我们使用 laravel 的 illuminatetestingassert 来进行断言。那么我们的测试就可以是这样的。

public function test_incrementor(): void{    Foo::fake();    Foo::foo();    Foo::foo();    Foo::foo();    Foo::assertFooCount(3); // pass!}

就是这样!

并不是所有东西都需要外观,但是当您构建内部使用的工具/包时,外观通常是一个值得依赖的强大模式。

这是包含所有代码的存储库:https://github.com/clintwinter/laravel-facade-example