【问题标题】:Laravel: Unit test Route::group with prefixLaravel:单元测试 Route::group 带前缀
【发布时间】:2017-04-14 17:26:32
【问题描述】:

我是 Laravel 的新手,正在关注 Pluralsight 中的 Play by Play: Getting Started with Laravel 5 with Steven Maguire 视频。这是 Vagrant 中 lavarvel/laravel 框的全新安装。 Laravel 版本为 5.4.15,PHPUnit 版本为 5.7.16。

但是,当我添加组和控制器时,我似乎无法让路由测试不失败。

这是我的routes/web.php 文件:

<?php
Route::get('products', ['as' => 'products', function(){
    return App\Product::all();
}]);

这是我的tests/Unit/ExampleTest.php

<?php

namespace Tests\Unit;

use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Http\Response;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testBasicTest()
    {
        $this->assertTrue(true);
    }
    public function testProductsList(){
        $this->get(route('products'))
            ->assertResponseOk();
    }
}

仅使用此基本设置,当我运行测试时,我会收到以下响应:Error: Call to undefined method Illuminate\Http\Response::assertResponseOk()

阅读后,我将测试重组为:

public function testProductsList(){
    $response = $this->get(route('products'));
    $this->assertEquals(200, $response->status());
}

这个新结构成功了,所以我继续看视频,接下来我们添加了一个路线组。这是routes\web.php 文件中的路由组:

Route::group(['prefix' => 'api'], function(){
    Route::get('products', ['as' => 'products', function(){
        return App\Product::all();
    }]);
});

这也成功了。当我将Route::resource 引入流程时,问题就开始了。测试开始失败。这是新的routes\web.php 文件:

Route::group(['prefix' => 'api'], function(){
    Route::resource('products', 'ProductController', ['only' => ['index', 'store', 'update']]);
});

这是此文件app/Http/Controllers/ProductController.php 中的ProductController 类:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ProductController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        //
    }
}

我使用php artisan make:controller ProductController 创建了这个控制器。这是我现在收到的错误:InvalidArgumentException: Route [products] not defined.

我尝试将调整后的testProductsList()方法改回原来的代码,还是不行。

对如何克服这些错误有任何见解吗?我宁愿保留原始的testProductsList() 方法代码,但是,如果它不再是这样工作的,那么只要我能让它工作,我就可以了。提前感谢您的帮助和洞察力!

编辑

根据评论,我使用命令php artisan make:controller ProductController --resource 删除并重新创建了ProductController 类。我已经在文件app/Http/Controllers/ProductController.php 中编辑了上述内容以反映更改。但是,测试仍然失败。有了亚历克斯的回答,我也将testProductsList 方法更改为此,但没有任何变化:

public function testProductsList(){
    /*
    * I tried just `products` for the route 
    * and still receive the error.
    */
    $this->get(route('api.products'));
    $this->assertResponseOk();
}

还有其他想法吗?

【问题讨论】:

  • 如果你要使用Route::resource(),你需要使用php artisan make:controller ProductController --resourceRoute::resource() 需要的控制器方法中缺少几个函数。

标签: php laravel phpunit


【解决方案1】:

尝试php artisan route:list 获取路线名称 我怀疑路由的名称不是products(可能是products.indexapi.products.index),因此出现错误。

有三种方法可以解决这个问题:

  1. 使用 route list artisan 命令显示的正确路线名称更新 $this-&gt;get(route('products'));(例如 $this-&gt;get(route('products.index'));
  2. 不要使用 route 辅助函数,而是直接引用路由,如下所示:$this-&gt;get('/api/products/index');
  3. 使用自定义名称重命名路由,如下所示:

    Route::resource('products', 'ProductController', 
        ['names' => ['index' => 'products.list', // etc...]]);
    

然后使用$this-&gt;get(route('products.list'));

【讨论】:

  • 我想避免将路线硬编码到测试中,以防将来路线发生变化,所以我尝试了选项 1 和 3,尽管它们都没有在测试方法$this-&gt;get(route('products.list')); $this-&gt;assertResponseOk();(我得到了关于assertResponseOk() 未定义的先前错误),我让它与上面列出的调整后的代码一起工作。谢谢!
【解决方案2】:

你错过了$this

public function testProductsList(){
    $this->get(route('products'));
    $this->assertResponseOk();
}

顺便说一句,这不是单元测试。见What's the difference between unit, functional, acceptance, and integration tests?

【讨论】:

  • 感谢您提供有关测试差异的提示!
【解决方案3】:

您可以像这样编辑 Route::group:

Route::group(['prefix' => 'api', 'as' => 'api.'], function(){
    Route::resource('products', 'ProductController', ['only' => ['index', 'store',     'update']]);
});

【讨论】: