[ACCEPTED]-Mocking Symfony2's request and session in PHPUnit-phpunit

Accepted answer
Score: 12

Not all code is worth unit testing. Usually 21 this is an indicator that your code could 20 be simplified. When you unit test code that 19 is somewhat complex the tests can become 18 a burden and normally it would be better 17 to do an integration of edge-to-edge test 16 in these cases. It's also not clear in your 15 example how your class gets the RequestStack so I will 14 assume that it has been injected in __construct.

With 13 that said here's how you would test that 12 code:

protected function setUp()
{
    $this->requestStack = $this->getMock('Fully-qualified RequestStack namespace');

    $this->SUT = new MyClass($this->requestStack);
}    

/** @test */
public function it_should_store_value_in_the_session()
{
    $value = 'test value';

    $request = $this->getMock('Request');
    $request->request = $this->getMock('ParameterBag');
    $session = $this->getMock('Session');

    $this->requestStack
        ->expects($this->atLeastOnce())
        ->method('getCurrentRequest')
        ->will($this->returnValue());

    $request->request
        ->expects($this->atLeastOnce())
        ->method('get')
        ->with('something')
        ->will($this->returnValue($value));

    $request
        ->expects($this->once())
        ->method('getSession')
        ->will($this->returnValue($session));

    $session
        ->expects($this->once())
        ->method('set')
        ->with('somevar', $value);

    $this->SUT->doSomething();
}

This should give you a starting point 11 but beware having a wall-of mocks in your 10 tests because very small changes to the 9 implementation details can cause your tests 8 to fail even though the behaviour is still 7 correct and this is something you want to 6 avoid as much as possible so the tests aren't 5 expensive to maintain.

Edit: I thought some 4 more about your question and realized that 3 typically you can inject the Session as 2 a dependency. If that's possible in your 1 use case it would simplify the tests a lot.

Score: 6

You don't need to mock RequestStack, it's a super simple 8 class. You can just create a fake request 7 and push it to it. You can also mock the 6 session.

// you can overwrite any value you want through the constructor if you need more control
$fakeRequest = Request::create('/', 'GET');

$fakeRequest->setSession(new Session(new MockArraySessionStorage()));
$requestStack = new RequestStack();
$requestStack->push($fakeRequest);
// then pass the requestStack to your service under test.

But in terms of testing, having 5 to mess around with the internals of a class 4 is not a good sign. Maybe you can create 3 a handler class to encapsulate the logic 2 you need from the request stack so you can 1 test more easily.

Score: 3

It's difficult to imagine a situation where 8 you'd have to be dealing with GET/POST parameters 7 inside a unit-tested class. Have the Controller 6 deal with HTTP requests and sessions (that's 5 pretty much what they're there for), and 4 pass the variables down into the relevant 3 classes to deal with the rest.

That being 2 said, Kevin's response is a possible solution 1 if you want to go down that route.

Score: 1

According to this: http://api.symfony.com/2.4/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.html

I got to work something 7 like the following:

public function testCompanySession()
{
    $Request = new Request();
    $Request->setSession(
        new Session(new MockArraySessionStorage())
    );

    $CompanySessionMapper = new CompanyMapper($Request);

    $Company = new Company();

    $Company->setName('test');

    $CompanySessionMapper->set($Company);

    $Company = new Company();

    $CompanySessionMapper->get($Company);

    $this->assertEquals($Company->getName(), 'test');
}

Only one test per object 6 type in my case since I'm only testing if 5 the session name is correct and retrieving/storing 4 the object properly in the session. CompanyMapper class 3 uses the session to store the company object 2 among other session/application related 1 functions.

Score: 0

Anyone coming from Google like me wants 4 to know how to mock request content, it 3 is as simple as:

use AppBundle\Controller\DefaultController;
use Symfony\Component\HttpFoundation\Request;
use PHPUnit\Framework\TestCase;

class DefaultControllerTest extends TestCase
{
    //@dataProvider?
    public function testWithMockedRequest()
    {
        //create a request mock
        $request = $this
            ->getMockBuilder(Request::class)
            ->getMock();

        //set the return value
        $request
            ->expects($this->once())
            ->method('getContent')
            ->will($this->returnValue('put your request data here'));

        //create your controller
        $controller = new DefaultController();

        //get the Response based on your Request
        $response = $controller->myRequestAction($request);

        //assert!
        $this->assertEquals(200, $response->getStatusCode());
     }
}

As you can see you can execute 2 a real controller which uses $request->getContent()

I hope this 1 helps someone.

More Related questions