在PHPUnit中模拟Symfony2的请求和会话

时间:2014-05-23 19:22:52

标签: symfony service mocking phpunit

我有一个类需要Symfony2服务@request_stack,它返回Symfony\Component\HttpFoundation\RequestStack的实例。我用它来检索POST和GET值。

此外,我的班级使用来自Symfony\Component\HttpFoundation\Session的{​​{1}}来调用当前会话。

现在我的班级有一个看起来像这样的方法:

Request->getSession()

我需要能够:

  1. 模拟class MyClass { public function doSomething() { //Get request from request stack. $Request = $this->RequestStack->getCurrentRequest(); //Get a variable from request $var = $Request->request->get('something'); //Processes $var into $someprocessedvar and lets say it's equal to 3. //Set value to session. $this->Request->getSession()->set('somevar', $someprocessedvar); } }
  2. RequestStack
  3. 获取Request
  4. RequestStack t;
  5. 获取Session

    所有这些说明如何测试Reques是否成功设置了会话中的预期值?

5 个答案:

答案 0 :(得分:8)

并非所有代码都值得单元测试。通常这表明您的代码可以简化。当您对单个测试代码进行单元测试时,测试可能会成为一种负担,通常情况下,在这些情况下进行边对边测试的集成会更好。在您的示例中,您的班级如何获得RequestStack也不清楚,因此我会假设它已被__construct注入。

说到这里,您将如何测试该代码:

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();
}

这应该给你一个起点,但要注意你的测试中有一堆模拟,因为实现细节的微小变化会导致你的测试失败,即使行为仍然是正确的,这是你想要避免的尽可能保持测试的成本并不高。

编辑:我想到了更多关于你的问题,并意识到通常你可以将Session作为依赖注入。如果在您的使用案例中可以实现,则会大大简化测试。

答案 1 :(得分:4)

你不需要模拟RequestStack,这是一个超级简单的类。您可以创建一个虚假请求并将其推送到它。你也可以模拟会话。

// 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.

但是在测试方面,不得不乱用一个类的内部并不是一个好兆头。也许您可以创建一个处理程序类来封装您需要的逻辑,以便您可以更轻松地进行测试。

答案 2 :(得分:3)

很难想象你必须在单元测试的类中处理GET / POST参数的情况。让Controller处理HTTP请求和会话(这几乎就是它们的用途),并将变量传递给相关的类来处理其余的。

话虽如此,凯文的回答是一个可能的解决方案,如果你想沿着这条路走下去。

答案 3 :(得分:0)

根据这个:http://api.symfony.com/2.4/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.html

我开始做以下工作:

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');
}

在我的情况下,每个对象类型只有一个测试,因为我只测试会话名称是否正确并在会话中正确检索/存储对象。 CompanyMapper类使用会话将公司对象存储在其他与会话/应用程序相关的函数中。

答案 4 :(得分:0)

像我这样来自谷歌的任何人都想知道如何模拟请求内容,它就像:

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());
     }
}

如您所见,您可以执行使用$request->getContent()

的真实控制器

我希望这有助于某人。