资讯专栏INFORMATION COLUMN

使用桩件 (Stub) 解决 Laravel 单元测试中的依赖

Miyang / 1194人阅读

摘要:可是我们在做单元测试的时候,希望尽可能少的产生依赖。后记任何一个可靠的系统,单元测试都是必不可少的。庆幸的是,帮我们提供了好用的单元测试。

本文是我在实践后的一点总结,难免有不妥之处。如有幸得大神路过,还望不吝赐教,小弟在此谢过了!

很早就知道有单元测试的概念,也曾尝试过,但是一直对单元测试的概念和方法,比较模糊。在听了 @vimac 大神的讲堂 PHP单元测试与测试驱动开发 后,慢慢地对单元测试和 PHPUnit 的认识清晰了起来,也开始慢慢地去实践单元测试。

Laravel 中的依赖

我们都知道,Laravel 使用了 IoC,各个模块之间也因此解耦了。而正是因为这一点,我们在 Laravel 中编写单元测试的时候,变得更加轻松了。

举个栗子

考虑以下场景。我们在开发中,可能会在控制器和模型之间加一个 Repository 来处理数据。那么我们的 Controller 就会依赖 Respository。利用 Laravel 的 IoC,我们可以定义一个 Service Provider 来集中将 Respository 注入到容器中。

假设我们现在有这样一个 Repository,里面记录了商品的信息,我们想要在 Controller 中获取某件商品信息,然后执行一些业务逻辑。

Class GoodRepository
{
    public function getGoodById($goodId)
    {
        // TODO: Get good by its id.
    }
}

class GoodController extends Controller
{
    public function show($id, GoodRepository $goodRepository)
    {
        // TODO: Do something with good info from that repository.
    }
}

// In route/api.php
Route::get("/api/good/{id}", "GoodController@show");

// Create a RepositoriesServiceProvider in Provider/RepositoriesServiceProvider.php。
// And inject the GoodRepository into Container.
class RepositoriesServiceProvider extends ServiceProvider
{
    public function boot()
    {
    
    }
    
    public function register()
    {
        $this->app->singleton(GoodRepository::class);
    }
}

好了,我们可以发现,GoodController 是依赖 GoodRepository 的,而 GoodRepository 是依赖数据库中的数据的。可是我们在做单元测试的时候,希望尽可能少的产生依赖。所以,我们应该希望能够掌控 GoodRepository 所返回的数据。

在 Laravel,提供了 $this->get("/path/to/route"); 的方法来对 HTTP 请求进行测试。这个测试必然会涉及到刚才所提到的那些依赖,如何解决这个依赖的问题,我们可以请出我们的主角————桩件。

桩件

将对象替换为(可选地)返回配置好的返回值的测试替身的实践方法称为上桩(stubbing)。

这是 PHPUnit 文档上 的解释。那我的理解呢,所谓的桩件,就是模拟一个依赖的类的行为,使得这个行为所做的事情在我们自己的掌控之中。比如上面的这种情况,我们希望模拟 GoodRepositorygetGoodById 方法返回与真正的返回结构相同的值,而不需要依赖外部数据源。

在 Laravel 中使用桩件

我们通过 Service Provider 注册了 GoodRepository 单例,那么按照这个思路,我们在写单元测试的时候,就可以将我们定义的桩件,注册为 GoodRepository 单例。

class GoodControllerTest extends TestCase
{
    public function testShow()
    {
        $data = []; // The data returns from GoodRepository::getGoodById.
        
        $stub = $this->createMock(GoodRepository::class);
        
        $stub->method("getGoodById")->will($this->returnValue($data));
        
        $this->app->singleton(GoodRepository::class, function () use ($stub) {
            return $stub;
        });
        
        $response = $this->get("/api/good/1");
        
        // Some assertions.
    }
}

我们通过在这里将桩件 $stub 用单例模式注册给了 Container,在调用 $this->get("/api/good/1"); 时原本在 Controller 中的 GoodRepository 依赖就变成了我们自定义的桩件 $stub。我们将 $data 定义为和返回值相同的结构,注册到桩件中。这样,所有的数据都在我们可控的范围了。

如果我们在这里不使用桩件,而是直接依赖外部(数据库)中的数据,那么如果 id 为 1 的数据被删除了,我们是不是就要改成 2 了呢?我们是不是就要重新计算数据了匹配断言了呢?这样的测试,可靠性便大大降低。

后记

任何一个可靠的系统,单元测试都是必不可少的。庆幸的是,PHPUnit 帮我们提供了好用的单元测试。本文所讲的,也只是 PHPUnit 的九牛一毛。而我自己也在慢慢摸索慢慢实践中。与君共勉。

最后还是推荐去听一下 @vimac 的讲堂 PHP单元测试与测试驱动开发,受益匪浅。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/25620.html

相关文章

  • Laravel核心——服务容器的细节特性

    摘要:前言首先欢迎关注我的博客在前面几个博客中,我详细讲了容器各个功能的使用绑定的源码解析的源码,今天这篇博客会详细介绍容器的一些细节,一些特性,以便更好地掌握容器的功能。 前言 首先欢迎关注我的博客: www.leoyang90.cn 在前面几个博客中,我详细讲了 Ioc 容器各个功能的使用、绑定的源码、解析的源码,今天这篇博客会详细介绍 Ioc 容器的一些细节,一些特性,以便更好地掌握容...

    AprilJ 评论0 收藏0
  • Java JUnit 单元测试小结

    摘要:一旦你完成了相应函数,只需要把注解删去,就可以进行正常的测试。表示该方法只执行一次,并且在所有方法之后执行。 测试类型 单元测试(Unit test) 单元测试关注单一的类. 它们存在的目的是检查这个类中的代码是否按照期望正确运行. 集成测试(Integration test) 顾名思义, 集成测试是检查开发的模块和其他模块整合时是否正常工作.虽然集成测试的代码影响范围比单元测试要广,...

    Rainie 评论0 收藏0
  • 【Node Hero】9. Node.js 单元测试

    摘要:基本上,测试金字塔描述你应该编写单元测试集成测试和端到端测试。集成测试要比端到端测试多,单元测试甚至要更多一些。应用程序单元测试编写单元测试,是为了看看给定的模块单元是否工作。 本文转载自:众成翻译译者:网络埋伏纪事链接:http://www.zcfy.cc/article/1754原文:https://blog.risingstack.com/node-hero-node-js-un...

    104828720 评论0 收藏0
  • PHP 单元测试

    摘要:上面的规定是必须遵守的,如果代码没有遵守规定不会把他当做单元测试代码。每个单元测试类以被测试的类名开头。每个单元测试函数应该为被测试函数名结尾。函数可能在测试环境不可执行,那么无法获取的执行结果,从而无法对进行单元测试。 本文首发于 https://jaychen.cc/article/34作者 Jaychen showImg(https://segmentfault.com/img/...

    娣辩孩 评论0 收藏0
  • Node.js面试题之2017

    摘要:译者按从标准,语法以及模块角度来看,的发展让人目不暇接,那么面试题也得与时俱进。因此,手动检查所有依赖是不现实的。为,加之后返回。自从年双十一正式上线,累计处理了亿错误事件,得到了金山软件百姓网等众多知名用户的认可。 译者按: 从ECMAScript标准,Node.js语法以及NPM模块角度来看,Node.js的发展让人目不暇接,那么面试题也得与时俱进。 原文: Node.js In...

    Alliot 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<