POST 方法的 Slim 3 集成测试始终不包括 Header

Slim 3 Integration Test For POST Method Always Not Included Header

我目前正在开发一个使用 Slim 3 框架编写的应用程序,并尝试使用 TDD 概念,但我面临几个问题,包括在使用 POST 方法测试时,header我嵌入的总是被认为是缺失的。

下面是我的代码

<?php

namespace Tests\routes\shopify;

use PHPUnit\Framework\TestCase;
use Slim\App;
use Slim\Http\Environment;
use Slim\Http\Headers;
use Slim\Http\Request;
use Slim\Http\RequestBody;
use Slim\Http\Response;
use Slim\Http\UploadedFile;
use Slim\Http\Uri;

class ShopifyRoutesTest extends TestCase
{
    private $app;

    protected function setUp(): void
    {
        // Use the application settings
        $settings = require __DIR__ . '/../../../src/settings.php';

        // Instantiate the application
        $this->app = new App($settings);

        // Set up dependencies
        $dependencies = require __DIR__ . '/../../../src/dependencies.php';
        $dependencies($this->app);

        // Register middleware
        $middleware = require __DIR__ . '/../../../src/middleware.php';
        $middleware($this->app);

        // Register routes
        $routes = require __DIR__ . '/../../../src/app/routes/api/shopify/routes.php';
        $routes($this->app);
    }

    public function testPostSyncProductBySkuWithEmptyApikeyShouldReturnBadRequest()
    {
        // Create a mock environment for testing with
        $environment = Environment::mock();
        $uri = Uri::createFromString("/channel/shopify/v1/product/sync-by-sku");
        $headers = new Headers(array(
            "Content-Type" => "application/json",
            "Authorization" => "client-apikey",
            "x-api-key" => ""
        ));
        $cookies = [];
        $serverParams = $environment->all();
        $body = new RequestBody();
        $uploadedFiles = UploadedFile::createFromEnvironment($environment);

        // Set up a request object based on the environment
        $request = new Request("POST", $uri, $headers, $cookies, $serverParams, $body, $uploadedFiles);

        $reqBody = array(
            "shop_name" => "STORE ABCE",
            "sku" => "SKU-001"
        );

        $body->write(json_encode($reqBody));


        // Add request data, if it exists
        $request = $request->withParsedBody($reqBody);
        // Set up a response object
        $response = new Response();

        $response = $this->app->process($request, $response);

        self::assertEquals(400, $response->getStatusCode());
        self::assertStringContainsString("Header: x-api-key cannot be empty", $response->getBody());
    }

    protected function tearDown(): void
    {
        $this->app = null;
    }
}


第一个断言成功,值为 400,但在第二个断言中失败,字符串不包含值 "Header:x-api-key 不能空 而是这个

Failed asserting that '{\n
    "error": "AUTH_FAIL",\n
    "errorDetail": "Header: Authorization, x-api-key required",\n
    "status": 400,\n
    "message": "BAD_REQUEST",\n
    "data": ""\n
}' contains "Header: x-api-key cannot be empty".

奇怪的是,当我 var_dump($request->getHeaders()) headers 值我提出的请求结果是那里

array(3) {
  ["Content-Type"]=>
  array(1) {
    [0]=>
    string(16) "application/json"
  }
  ["Authorization"]=>
  array(1) {
    [0]=>
    string(13) "client-apikey"
  }
  ["x-api-key"]=>
  array(1) {
    [0]=>
    string(0) ""
  }
}

我还尝试使用 Postman 测试我的 API 端点,结果符合预期

请求

curl --location --request POST 'http://localhost:8080/channel/shopify/v1/product/sync-by-sku' \
--header 'Authorization: client-apikey' \
--header 'x-api-key: 1293129382938' \
--header 'Content-Type: application/json' \
--header 'Cookie: PHPSESSID=tll8s24tp253rda1harv0koapi' \
--data-raw '{
    "shop_name" : "STORE ABC",
    "sku" : "SKU-991"
}'

回应

{
    "error": "AUTH_FAIL",
    "errorDetail": "Header: x-api-key cannot be empty",
    "status": 400,
    "message": "BAD_REQUEST",
    "data": ""
}

此外,我已经阅读了此处描述的来自 stakoverflow 的答案

但是我还是找不到解决方案,header总是被认为是丢失了。非常感谢这个问题的解决方案,提前谢谢你

终于搞清楚了Slim 3中Header和Request的结构和行为,Slim 3中的Header class总是把key值变成小写,不知道是什么意思意味着,但最后我需要在我的中间件中调整这种行为,从以前使用的 $request->getHeaders() 到 $request->getHeaderLine() 以及 $request->hasHeader(), $request ->getHeaders() 将header值设为大写并在key前面加上HTTP_

在我的例子中,这是问题的原因,因为我在单元测试中使用的请求必须通过小写值,并且密钥前面没有 HTTP_,所以中间件假设应该存在的key根本不存在

之前的中间件

// Common Channel Auth with client-apikey
    $app->add(function (Request $request, Response $response, callable $next) use ($container) {
        $uri = $request->getUri();
        $path = $uri->getPath();
        $headers = $request->getHeaders();
        $arrayPath = explode("/", $path);

        if ($arrayPath[1] == "channel" && $arrayPath[3] == "tools")
        {
            return $next($request, $response);
        }
        elseif ($arrayPath[1] == "channel" && $arrayPath[4] != "webhook")
        {
            /** @var ClientRepository $clientRepository */
            $clientRepository = $container->get("ClientRepository");

            // Get Header With Name x-api-key & Authorization
            if (isset($headers["HTTP_AUTHORIZATION"]) && isset($headers["HTTP_X_API_KEY"]))
            {
                if ($headers["HTTP_AUTHORIZATION"][0] == "client-apikey")
                {
                    $reqClientApikey = $headers["HTTP_X_API_KEY"][0];

                    if (v::notBlank()->validate($reqClientApikey))
                    {
                        if ($clientRepository->findByClientApiKey($reqClientApikey))
                        {
                            return $next($request, $response);

之后的中间件
// Common Channel Auth with client-apikey
    $app->add(function (Request $request, Response $response, callable $next) use ($container) {
        $uri = $request->getUri();
        $path = $uri->getPath();
        $arrayPath = explode("/", $path);

        if ($arrayPath[1] == "channel" && $arrayPath[3] == "tools")
        {
            return $next($request, $response);
        }
        elseif ($arrayPath[1] == "channel" && $arrayPath[4] != "webhook")
        {
            /** @var ClientRepository $clientRepository */
            $clientRepository = $container->get("ClientRepository");

            // Using $request-hasHeader & $request->getHeaderLine instead of $headers["HTTP_AUTHORIZATION"]
            if ($request->hasHeader("authorization") != null && $request->hasHeader("x-api-key") != null)
            {
                if ($request->getHeaderLine("authorization") == "client-apikey")
                {
                    $reqClientApikey = $request->getHeaderLine("x-api-key");

                    if (v::notBlank()->validate($reqClientApikey))
                    {
                        if ($clientRepository->findByClientApiKey($reqClientApikey))
                        {
                            return $next($request, $response);
                        }

单元测试

public function testPostSyncProductBySkuWithEmptyApikeyShouldReturnBadRequest()
    {
        // Create a mock environment for testing with
        $environment = Environment::mock();
        $uri = Uri::createFromString("/channel/shopify/v1/product/sync-by-sku");
        $headers = new Headers([
            "Content-Type" => "application/json",
            "Authorization" => "client-apikey",
            "x-api-key" => ""
        ]);
        $cookies = [];
        $serverParams = $environment->all();
        $body = new RequestBody();
        $uploadedFiles = UploadedFile::createFromEnvironment($environment);

        // Set up a request object based on the environment
        $request = new Request("POST", $uri, $headers, $cookies, $serverParams, $body, $uploadedFiles);

        $reqBody = array(
            "shop_name" => "STORE ABCE",
            "sku" => "SKU-001"
        );

        $body->write(json_encode($reqBody));


        // Add request data, if it exists
        $request = $request->withParsedBody($reqBody);
        // Set up a response object
        $response = new Response();

        $response = $this->app->process($request, $response);

        self::assertEquals(400, $response->getStatusCode());
        self::assertStringContainsString("Header: x-api-key cannot be empty", $response->getBody());
    }

再一次,希望我犯的这个错误能成为别人的记录,谢谢