Я работаю над небольшим личным проектом по созданию игрушечного фреймворка / самоуверенного набора шаблонов, в основном для образовательных целей, который, по сути, представляет собой просто серию готовых компонентов и некоторый базовый клей для их склеивания. Вы можете найти его на Github . Однако у меня возникли некоторые проблемы с добавлением интеграционных тестов - в прошлом я обычно полагался на то, что мои фреймворки уже работают для меня.
Мне пришлось пока пропустить один тест, потому что он проходит только в том случае, если выполняется в отдельном процессе. Это наводит меня на мысль, что с разрывом что-то не так, но я не смог понять, что это такое.
Существует базовый тестовый пример, который выглядит так:
<?php declare(strict_types = 1);
namespace Tests;
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
class TestCase extends \PHPUnit\Framework\TestCase
{
use MockeryPHPUnitIntegration;
public function setUp(): void
{
if (!defined('BASE_DIR')) {
define('BASE_DIR', __DIR__.'/../');
}
$this->app = new \App\Kernel;
$this->app->bootstrap();
$this->container = $this->app->getContainer();
}
public function tearDown(): void
{
$this->app = null;
$this->container = null;
}
}
Затем интеграционный тестовый пример, который расширяет его и выглядит так:
<?php declare(strict_types = 1);
namespace Tests;
use Zend\Diactoros\ServerRequest;
class IntegrationTestCase extends TestCase
{
public function makeRequest(string $uri, string $method = 'GET', $server = [], $files = [], $body = 'php://input', $headers = [], $cookies = [], $queryParams = [], $parsedBody = null): IntegrationTestCase
{
$request = new ServerRequest(
$server,
$files,
$uri,
$method,
$body,
$headers,
$cookies,
$queryParams,
$parsedBody
);
$this->response = $this->app->handle($request);
return $this;
}
public function assertStatusCode(int $code, $message = ''): void
{
if (!isset($this->response)) {
throw new \Exception('No response has been received');
}
self::assertThat($this->response->getStatusCode() == $code, self::isTrue(), $message);
}
public function tearDown(): void
{
$this->response = null;
parent::tearDown();
}
}
Наконец, фактический класс интеграционных испытаний выглядит следующим образом:
<?php declare(strict_types = 1);
namespace Tests\Integration;
use Tests\IntegrationTestCase;
class ExampleTest extends IntegrationTestCase
{
public function testHome(): void
{
$this->makeRequest('/')
->assertStatusCode(200);
}
public function testLogin(): void
{
$this->markTestSkipped();
$this->makeRequest('/login')
->assertStatusCode(200);
}
public function test404(): void
{
$this->makeRequest('/foo')
->assertStatusCode(404);
}
}
Второй тест (в настоящее время пропущенный), когда он запускается отдельно или в отдельном процессе, работает нормально. Однако при нормальном запуске второй тест выдает 404. Если я поменяю местами первые два теста, новый второй провалится, что говорит мне, что это как-то связано с процессом разборки, но я не смог его отследить вниз еще. Это также может быть то, как я использую маршрутизатор (пакет Route от PHP League).
Проблема вполне может быть связана с классом Kernel
, воспроизведенным ниже:
<?php declare(strict_types = 1);
namespace App;
use Zend\Diactoros\ServerRequestFactory;
use League\Container\Container;
use League\Container\ReflectionContainer;
use League\Route\Strategy\ApplicationStrategy;
use Psr\Http\Message\RequestInterface;
/**
* Application kernel
*/
class Kernel
{
/**
* @var Container
*/
private $container;
/**
* @var Router
*/
private $router;
/**
* @var Providers
*/
private $providers = [
'App\Providers\ContainerProvider',
'App\Providers\CacheProvider',
'App\Providers\DoctrineProvider',
'App\Providers\EventProvider',
'App\Providers\FlysystemProvider',
'App\Providers\HandlerProvider',
'App\Providers\LoggerProvider',
'App\Providers\RouterProvider',
'App\Providers\SessionProvider',
'App\Providers\ShellProvider',
'App\Providers\TwigProvider',
];
/**
* Bootstrap the application
*
* @return Kernel
*/
public function bootstrap(): Kernel
{
$this->setupContainer();
$this->setupRoutes();
$this->setErrorHandler();
return $this;
}
/**
* Handle a request
*
* @param RequestInterface $request HTTP request.
* @return void
*/
public function handle(RequestInterface $request): \Psr\Http\Message\ResponseInterface
{
try {
$response = $this->router->dispatch($request, $this->container->get('response'));
} catch (\League\Route\Http\Exception\NotFoundException $e) {
$twig = $this->container->get('Twig_Environment');
$tpl = $twig->load('404.html');
$response = $this->container->get('response')->withStatus(404);
$response->getBody()->write($tpl->render());
}
return $response;
}
private function setupContainer(): void
{
$container = new Container;
$container->delegate(
new ReflectionContainer
);
foreach ($this->providers as $provider) {
$container->addServiceProvider($provider);
}
$container->share('emitter', \Zend\Diactoros\Response\SapiEmitter::class);
$container->share('response', \Zend\Diactoros\Response::class);
$container->share('Psr\Http\Message\ResponseInterface', \Zend\Diactoros\Response::class);
$this->container = $container;
}
private function setErrorHandler(): void
{
error_reporting(E_ALL);
$environment = getenv('APP_ENV');
$whoops = new \Whoops\Run;
if ($environment !== 'production') {
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
} else {
$handler = $this->container->get('App\Contracts\Exceptions\Handler');
$whoops->pushHandler(new \Whoops\Handler\CallbackHandler($handler));
}
$whoops->register();
}
private function setupRoutes(): void
{
$strategy = (new ApplicationStrategy)->setContainer($this->container);
$router = $this->container->get('League\Route\Router')
->setStrategy($strategy);
require_once BASE_DIR.'/routes.php';
$this->router = $router;
}
public function getContainer(): Container
{
return $this->container;
}
}
Этот класс является ядром шаблонной таблицы - он загружается с помощью метода bootstrap()
, после чего вы передаете ему объект запроса Zend Diactoros, получая объект ответа обратно.
Есть идеи, почему выдает эту ошибку?