skip to Main Content

I am trying to reproduce the behaviour of the facebook batch requests function on their graph api.

So I think that the easiest solution is to make several requests on a controller to my application like:

public function batchAction (Request $request)
{
    $requests = $request->all();
    $responses = [];

    foreach ($requests as $req) {
        $response = $this->get('some_http_client')
            ->request($req['method'],$req['relative_url'],$req['options']);

        $responses[] = [
            'method' => $req['method'],
            'url' => $req['url'],
            'code' => $response->getCode(),
            'headers' => $response->getHeaders(),
            'body' => $response->getContent()
        ]
    }

    return new JsonResponse($responses)
}

So with this solution, I think that my functional tests would be green.

However, I fill like initializing the service container X times might make the application much slower. Because for each request, every bundle is built, the service container is rebuilt each time etc…

Do you see any other solution for my problem?

In other words, do I need to make complete new HTTP requests to my server to get responses from other controllers in my application?

Thank you in advance for your advices!

2

Answers


  1. There are ways to optimise test-speed, both with PHPunit configuration (for example, xdebug config, or running the tests with the phpdbg SAPI instead of including the Xdebug module into the usual PHP instance).

    Because the code will always be running the AppKernel class, you can also put some optimisations in there for specific environments – including initiali[zs]ing the container less often during a test.

    I’m using one such example by Kris Wallsmith. Here is his sample code.

    class AppKernel extends Kernel
    {
    // ... registerBundles() etc
    // In dev & test, you can also set the cache/log directories 
    // with getCacheDir() & getLogDir() to a ramdrive (/tmpfs).
    // particularly useful when running in VirtualBox
    
    protected function initializeContainer()
    {
        static $first = true;
    
        if ('test' !== $this->getEnvironment()) {
            parent::initializeContainer();
            return;
        }
    
        $debug = $this->debug;
    
        if (!$first) {
            // disable debug mode on all but the first initialization
            $this->debug = false;
        }
    
        // will not work with --process-isolation
        $first = false;
    
        try {
            parent::initializeContainer();
        } catch (Exception $e) {
            $this->debug = $debug;
            throw $e;
        }
    
        $this->debug = $debug;
    }
    
    Login or Signup to reply.
  2. Internally Symfony handle a Request with the http_kernel component. So you can simulate a Request for every batch action you want to execute and then pass it to the http_kernel component and then elaborate the result.

    Consider this Example controller:

    /**
     * @Route("/batchAction", name="batchAction")
     */
    public function batchAction()
    {
        // Simulate a batch request of existing route
        $requests = [
            [
                'method' => 'GET',
                'relative_url' => '/b',
                'options' => 'a=b&cd',
            ],
            [
                'method' => 'GET',
                'relative_url' => '/c',
                'options' => 'a=b&cd',
            ],
        ];
    
        $kernel = $this->get('http_kernel');
    
        $responses = [];
        foreach($requests as $aRequest){
    
            // Construct a query params. Is only an example i don't know your input
            $options=[];
            parse_str($aRequest['options'], $options);
    
            // Construct a new request object for each batch request
            $req = Request::create(
                $aRequest['relative_url'],
                $aRequest['method'],
                $options
            );
            // process the request
            // TODO handle exception
            $response = $kernel->handle($req);
    
            $responses[] = [
                'method' => $aRequest['method'],
                'url' => $aRequest['relative_url'],
                'code' => $response->getStatusCode(),
                'headers' => $response->headers,
                'body' => $response->getContent()
            ];
        }
        return new JsonResponse($responses);
    }
    

    With the following controller method:

    /**
     * @Route("/a", name="route_a_")
     */
    public function aAction(Request $request)
    {
        return new Response('A');
    }
    
    /**
     * @Route("/b", name="route_b_")
     */
    public function bAction(Request $request)
    {
        return new Response('B');
    }
    
    /**
     * @Route("/c", name="route_c_")
     */
    public function cAction(Request $request)
    {
        return new Response('C');
    }
    

    The output of the request will be:

    [
    {"method":"GET","url":"/b","code":200,"headers":{},"body":"B"},
    {"method":"GET","url":"/c","code":200,"headers":{},"body":"C"}
    ]
    

    PS: I hope that I have correctly understand what you need.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search