PHPUnit:预期状态码 200 但使用 Laravel 收到 419

Posted

技术标签:

【中文标题】PHPUnit:预期状态码 200 但使用 Laravel 收到 419【英文标题】:PHPUnit: Expected status code 200 but received 419 with Laravel 【发布时间】:2018-03-01 17:38:57 【问题描述】:

我想测试删除方法,但我没有从 phpUnit 获得预期的结果。我在运行测试时收到此消息:

 Expected status code 200 but received 419. Failed asserting that false is true.
 /vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:77
 /tests/Unit/CategoriesControllerTest.php:70

Laravel 版本:5.5

感谢您的帮助!

控制器构造函数:

public function __construct()

    $this->middleware('auth');

    $this->middleware('categoryAccess')->except([
        'index',
        'create'
    ]);

控制器方法:

public function destroy($categoryId)

    Category::destroy($categoryId);

    session()->flash('alert-success', 'Category was successfully deleted.');

    return redirect()->action('CategoriesController@index');

categoryAccess 中间件:

public function handle($request, Closure $next)

    $category = Category::find($request->id);

    if (!($category->user_id == Auth::id())) 
        abort(404);
    

    return $next($request);

类别型号:

protected $dispatchesEvents = [
    'deleted' => CategoryDeleted::class,
];

事件监听器

public function handle(ExpensesUpdated $event)

    $category_id = $event->expense->category_id;

    if (Category::find($category_id)) 
        $costs = Category::find($category_id)->expense->sum('cost');

        $category = Category::find($category_id);

        $category->total = $costs;

        $category->save();
    

PHPUnit 删除测试:

use RefreshDatabase;

protected $user;

public function setUp()

   parent::setUp();
   $this->user = factory(User::class)->create();
   $this->actingAs($this->user);


/** @test */
public function user_can_destroy()

    $category = factory(Category::class)->create([
        'user_id' => $this->user->id
    ]);

    $response = $this->delete('/category/' . $category->id);

    $response->assertStatus(200);

    $response->assertViewIs('category.index');

【问题讨论】:

这是一个身份验证问题,试试$this->withoutMiddleware();看看会不会OK!! 您好,谢谢,我将其添加到方法中并再次运行测试,我现在收到此消息:预期状态代码 200 但收到 302。 这次试试 PHPUnit 类中的 use WithoutMiddleware;,别忘了导入它 use Illuminate\Foundation\Testing\WithoutMiddleware; 谢谢@Maraboc $this->withoutMiddleware();作品!问题是这个测试应该期待状态码 302 我通过阅读来自 here 的答案发现的。 很高兴知道 ;) 【参考方案1】:

解决方案:当您缓存配置文件时,您可以通过运行php artisan config:clear 来解决此问题。

说明:这可以解决问题的原因是 PHPUnit 将使用缓存的配置值而不是在您的测试环境中定义的变量。因此,APP_ENV 未设置为测试,VerifyCsrfTokenMiddleware 将抛出 TokenMismatchException(状态代码 419)。

APP_ENV 设置为测试时,它不会抛出此异常,因为VerifyCsrfTokenMiddlewarehandle 方法会检查您是否使用$this->runningUnitTests() 运行单元测试。

建议不要在您的开发环境中缓存您的配置。当您需要在同时运行单元测试的环境中缓存配置时,您可以在 TestCase.php 中手动清除缓存:

use Illuminate\Support\Facades\Artisan; 

public function createApplication()

    ....
    Artisan::call('config:clear')
    ....

基于https://github.com/laravel/framework/issues/13374#issuecomment-239600163的示例

在此blog post 或Laravel documentation 中阅读有关配置缓存的更多信息。

【讨论】:

这对我也有用,但为什么这能解决问题? @DarrenFindlay 我已经添加了一些解释。【参考方案2】:

这是确切的解决方案:

Laravel 环境将在启动应用程序后设置,因此您无法从 appServiceProvider 或其他来源更改它。 要修复此错误,您需要将此函数添加到 App\Http\Middleware\VerifyCsrfToken

public function handle($request, \Closure $next)
    
        if(env('APP_ENV') !== 'testing')
        
            return parent::handle($request, $next);
        

        return $next($request);
    

您需要使用 .env.testing 文件中设置的 env('APP_ENV')

APP_ENV=testing

【讨论】:

【参考方案3】:

这里的消息确实和CSRF中间件有关。但是有一个比禁用中间件更好的方法来解决这个问题。

中间件带有内置代码,用于检测它是否在测试中使用。此检查查找 2 件事:

我是通过命令行运行的吗 我是否在testing 的环境类型中运行

默认,正确设置软件会导致在运行 PHP 单元时两个标志都为真。但是,最可能的罪魁祸首是您的APP_ENV 中的值。导致此错误的常见方式包括:

phpunit.xml 文件配置错误。它应该包含<server name="APP_ENV" value="testing" />APP_ENV 中设置了显式值的 shell 会话,该值会覆盖此值 在APP_ENV 中设置了显式值的 Docker/docker-compose/kubernetes 会话。如果可能的话,查看通过 .env 和/或 phpunit.xml 文件设置此值可能会更好。或者确保构建/测试过程设置值。

这也难倒了我,我不相信我需要使用WithoutMiddleware,因为我没有用于其他项目,但事实证明我已经在命令行上尝试了一些东西并覆盖了@987654328 @ 在 bash 中。

【讨论】:

【参考方案4】:

修改文件app/Http/Middleware/VerifyCsrfToken.php,添加:

public function handle($request, \Closure $next)

    if ('testing' !== app()->environment())
    
        return parent::handle($request, $next);
    

    return $next($request);

来源:https://laracasts.com/discuss/channels/testing/trouble-testing-http-verbs-with-phpunit-in-laravel/replies/29878

【讨论】:

【参考方案5】:

有时在测试中您需要禁用中间件才能继续:

use Illuminate\Foundation\Testing\WithoutMiddleware;

class ClassTest extends TestCase

    use WithoutMiddleware; // use this trait

    //tests here

如果您只想针对一项特定的测试用途禁用它们:

$this->withoutMiddleware();

【讨论】:

这有效,除非中间件实际上是您的测试的一部分。

以上是关于PHPUnit:预期状态码 200 但使用 Laravel 收到 419的主要内容,如果未能解决你的问题,请参考以下文章

NSLocalizedDescription=(200-299)中的预期状态码,得到 422

NSLocalizedDescription=(200-299) 中的预期状态码,得到 400

AJAX请求服务器,响应状态码为200,但调用error函数

phpunit 测试检查状态码

RestKit .20 RKRequestDescriptor postObject - (400-499) 中的预期状态代码,得到 200

为啥当我运行我的 phpunit 测试时,我得到一个空响应,但 Postman 中的相同路由/用户的行为符合预期?