【问题标题】:PHPUnit: Expected status code 200 but received 419 with LaravelPHPUnit:预期状态码 200 但使用 Laravel 收到 419
【发布时间】: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 的答案发现的。
  • 很高兴知道 ;)

标签: laravel phpunit


【解决方案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 postLaravel 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();
        

        【讨论】:

        • 这有效,除非中间件实际上是您的测试的一部分。
        猜你喜欢
        • 2021-05-24
        • 2019-03-20
        • 2018-11-26
        • 2020-08-07
        • 2016-08-18
        • 1970-01-01
        • 2019-08-01
        • 1970-01-01
        • 2017-01-20
        相关资源
        最近更新 更多