Laravel 编写 HTTP 测试时中间件共享变量视图不起作用

Laravel middleware sharing variable view does not work when writing HTTP test

我们有以下中间件。我们知道中间件是有效的,因为在浏览器中测试应用程序是有效的。可悲的是,在编写 HTTP 测试用例时,blade 说中间件定义的变量不存在。

<?php

namespace App\Http\Middleware;

use App\Repository\UserContract;
use Closure;
use Illuminate\Contracts\View\Factory as ViewFactory;
use Illuminate\Support\Facades\View;

class Authenticate
{
    private $userRepository;
    private $view;

    public function __construct(UserContract $userRepository, ViewFactory $view)
    {
        $this->userRepository = $userRepository;
        $this->view = $view;
    }

    public function handle($request, Closure $next)
    {
        $userId = null;
        $username = null;
        if ($request->hasCookie('auth')) {
            $secret = $request->cookie('auth');
            $userId = $this->userRepository->getUserIdBySecret($secret);
            $username = $this->userRepository->getUsername($userId);
        }
        $this->view->share('username', $username);
        $request['_user_id'] = $userId;
        $request['_username'] = $username;
        return $next($request);
    }
}

我们做一个 PHPUnit 测试如下:

    /**
     * @test
     */
    public function it_shows_logged_in_username()
    {
        $app = $this->createApplication();
        $encrypter = $app->get(Encrypter::class);
        $userRepository = $app->make(UserContract::class);
        $userRepository->addUser('jane', 'secret');

        $secret = $encrypter->encrypt('secret', false);
        $response = $this->call('GET', '/', [], ['auth' => $secret], [], [], null);
        $response->assertSeeText('jane');
    }

错误

ErrorException {#980                                                                                                                                           
  #message: "Undefined variable: username"                                                                                                                     
  #code: 0                                                                                                                                                     
  #file: "./storage/framework/views/db4b9232a3b0957f912084f26d9041e8a510bd6c.php"
  #line: 3                                                                     
  #severity: E_NOTICE                                                          
  trace: {                                                                     
    ./storage/framework/views/db4b9232a3b0957f912084f26d9041e8a510bd6c.php:3 {
      › <?php dump($comments); ?>                                              
      › <?php dump($username); ?>  

有什么建议吗?

编辑

我必须补充一点,使用 View 门面并在其上调用 share 方法使其工作,但我遇到了与应设置的 errors 变量相同的问题通过 \Illuminate\View\Middleware\ShareErrorsFromSession stock laravel 中间件。

其他说明,我的中间件名为 Authenticate 但它与 Laravel.

的正常基于密码的身份验证无关

这里是UserContract:

<?php
declare(strict_types=1);


namespace App\Repository;


use App\Repository\Exception\UserAlreadyRegisteredException;

interface UserContract
{
    public function isRegistered(string $username): bool;

    public function getUserID(string $username): int;

    public function getUserIdBySecret(string $secret): int;

    /**
     * @param int $userId
     *
     * @return string
     * @throws
     */
    public function getUsername(int $userId): string;

    /**
     * @param int $userId
     *
     * @return string
     * @throws AuthSecretNotSetException
     */
    public function getUserAuthSecret(int $userId): string;

    public function getNextId(): int;

    /**
     * @param string $username
     * @param string $authSecret
     *
     * @return int
     * @throws UserAlreadyRegisteredException
     */
    public function addUser(string $username, string $authSecret): int;

    public function fetchUpdatedBetween(?\DateTimeInterface $start, ?\DateTimeInterface $end): array;

    public function markAsSynced(int $userId): void;

    public function isSynced(int $userId): bool;

    public function delete(int $userId): void;

    public function markAsDeleted(int $userId): void;

    public function fetchDeletedIds(): array;

    public function removeDeletedFlag(int $userId): void;

    public function fetchStalledIds(\DateTimeInterface $dateTime): array;

    public function purge(int $userId): void;

    /**
     * @param UserFetcherContract $fetcher the closure would be passed userId and it should return data or null
     */
    public function setFallbackFetcher(UserFetcherContract $fetcher): void;
}

尝试view()->share('username', $username)

要正确测试登录,您应该提交 post 请求,因此请更改:

$response = $this->call('GET', '/', [], ['auth' => $secret], [], [], null);

$response = $this->call('POST', '/', [], ['auth' => $secret], [], [], null);




或者,如果您只想测试视图而不是登录表单本身,您可以使用 actingAs 方法...

创建时在变量中捕获用户:

$user = $userRepository->addUser('jane', 'secret');

使用 actingAs 方法测试已登录的视图 $user:

$this->actingAs($user)->get('/')->assertSeeText('jane');;

我终于花时间深入研究了这一点。

事实证明问题正在发生,因为我正在创建启动应用程序的多个实例,并且它以某种方式与应用程序中注册的视图工厂实例混淆。

在测试用例中删除此行使其工作:

/**
     * @test
     */
    public function it_shows_logged_in_username()
    {
        // THIS IS WRONG
        // $app = $this->createApplication();
        $app = $this->app; // Use application instantiated in setUp method of test case
        $encrypter = $app->get(Encrypter::class);
        $userRepository = $app->make(UserContract::class);
        $userRepository->addUser('jane', 'secret');

        $secret = $encrypter->encrypt('secret', false);
        $response = $this->call('GET', '/', [], ['auth' => $secret], [], [], null);
        $response->assertSeeText('jane');
    }

我不是 100% 确定为什么启动应用程序的多个实例会产生问题,但我的直觉是:某处有一些共享数据...

任何对这里感兴趣的人 commit