当前位置: 首页 > 后端技术 > PHP

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

时间:2023-03-30 01:26:25 PHP

Laravel单元测试中使用存根(Stub)解决依赖问题如果有幸有大神路过,希望大神指教,谢谢大哥!很早就知道单元测试的概念,也尝试过,但是单元测试的概念和方法一直比较模糊。听了@vimac关于PHP单元测试和测试驱动开发的讲座,逐渐对单元测试和PHPUnit有了清晰的认识,开始慢慢实践单元测试。我们都知道Laravel中的依赖关系。Laravel使用IoC,它解耦了模块。而也正是因为如此,当我们在Laravel中编写单元测试时,变得更加容易。例如,请考虑以下场景。在我们的开发中,我们可能会在controller和model之间添加一个Repository来处理数据。那么我们的Controller就会依赖于Respository。使用Laravel的IoC,我们可以定义一个ServiceProvider来将Respository集中注入到容器中。假设我们现在有这样一个Repository,里面记录了商品信息,我们想在Controller中获取某个商品信息,然后执行一些业务逻辑。ClassGoodRepository{publicfunctiongetGoodById($goodId){//TODO:通过其id获得好处。}}classGoodControllerextendsController{publicfunctionshow($id,GoodRepository$goodRepository){//TODO:使用该存储库中的有用信息做一些事情。}}//在route/api.phpRoute::get('/api/good/{id}','GoodController@show');//在Provider/RepositoriesServiceProvider.php中创建一个RepositoriesServiceProvider。//并将GoodRepository注入Container.classRepositoriesServiceProviderextendsServiceProvider{publicfunctionboot(){}publicfunctionregister(){$this->app->singleton(GoodRepository::class);}}那么,我们可以发现,GoodController依赖于GoodRepository,而GoodRepository依赖于数据库中的数据。但是我们在做单元测试的时候,希望产生的依赖越少越好。所以,我们应该希望能够控制GoodRepository返回的数据。在Laravel中,$this->get('/path/to/route');提供了测试HTTP请求的方法。这个测试难免会涉及到刚才说的依赖。如何解决这个依赖的问题,就可以请来我们的主角---stubs了。存根将对象替换为(可选)返回已配置返回值的测试替身的做法称为存根。这在PHPUnit文档中有解释。我的理解,所谓存根就是模拟一个依赖类的行为,让这个行为做什么是我们自己控制的。比如上面的案例,我们想模拟GoodRepository的getGoodById方法,在不依赖外部数据源的情况下,返回与真实返回结构相同的值。在Laravel中使用存根,我们通过ServiceProvider注册了GoodRepository单例,那么按照这个思路,我们在写单元测试的时候,可以将我们定义的存根注册为GoodRepository单例。classGoodControllerTestextendsTestCase{publicfunctiontestShow(){$data=[];//数据从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');//一些断言。我们这里以单例模式将存根$stub注册到Container中,然后调用$this->get('/api/good/1');当控制器中原来的GoodRepository依赖成为我们自定义的$stub时。我们将$data定义为与返回值相同的结构,并将其注册到存根中。这样所有的数据都在我们可控的范围内。如果我们这里不使用stubs,而是直接依赖外部(数据库)中的数据,那么如果删除了id为1的数据,是不是还要改成2呢?我们是否要重新计算数据以匹配断言?这种测试的可靠性大大降低。后记任何可靠的系统,单元测试都是必不可少的。幸运的是,PHPUnit为我们提供了易于使用的单元测试。本文所讲的只是PHPUnit的九牛一毛。而我也在慢慢摸索和练习。与你分享。最后推荐听听@vimac关于PHP单元测试和测试驱动开发的讲座,受益匪浅。