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
我们有以下中间件。我们知道中间件是有效的,因为在浏览器中测试应用程序是有效的。可悲的是,在编写 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