2012-07-17 34 views
4

我目前正在使用Symfony2,并且正在用PHPUnit测试我的项目。 我想用错误的参数提交表单或者URL不完整时测试异常。用symfony2和phpunit修改表单动作

我去了通过Symfony2和PHPUnit的文档,但没有找到任何类/方法来这样做。

如何更改表单动作的值?我想使用PHPUnit,所以创建的报告是最新的,我可以看到我的代码的覆盖范围。

编辑:

为了澄清我的问题,一些新的内容。 如何在我的控制器中测试以'>'开头的行? (throw $this->createNotFoundException('Unable to find ParkingZone entity.');

当用户修改操作链接时,控制器中的进程将通过异常(或错误消息,如果选择此操作)。我如何测试这种情况?

控制器

/** 
* Edits an existing ParkingZone entity. 
* 
* @Route("/{id}/update", name="parkingzone_update") 
* @Method("post") 
* @Template("RatpGarageL1Bundle:ParkingZone:edit.html.twig") 
*/ 
public function updateAction($id) 
{ 
    $em = $this->getDoctrine()->getEntityManager(); 

    $entity = $em->getRepository('RatpGarageL1Bundle:ParkingZone')->find($id); 

    if (!$entity) { 
>  throw $this->createNotFoundException('Unable to find ParkingZone entity.'); 
    } 

    $editForm = $this->createForm(new ParkingZoneType(), $entity); 
    $deleteForm = $this->createDeleteForm($id); 

    $request = $this->getRequest(); 

    $editForm->bindRequest($request); 

    if ($editForm->isValid()) { 
     $em->persist($entity); 
     $em->flush(); 

     return $this->redirect($this->generateUrl('parkingzone_edit', array('id' => $id))); 
    } 

    return array(
     'entity'  => $entity, 
     'edit_form' => $editForm->createView(), 
     'delete_form' => $deleteForm->createView(), 
    ); 
} 

检视:

<form action="{{ path('parkingzone_update', { 'id': entity.id }) }}" method="post" {{ form_enctype(form) }}> 
    <div class="control-group"> 
     {{ form_label(form.name, 'Name', { 'attr': {'class': 'control-label'} }) }} 
     <div class="controls error"> 
      {{ form_widget(form.name, { 'attr': {'class': ''} }) }} 
      <span class="help-inline">{{ form_errors(form.name) }}</span> 
     </div> 
    </div> 
    <div class="control-group"> 
     {{ form_label(form.orderSequence, 'Rang', { 'attr': {'class': 'control-label'} }) }} 
     <div class="controls error"> 
      {{ form_widget(form.orderSequence, { 'attr': {'class': ''} }) }} 
      <span class="help-inline">{{ form_errors(form.orderSequence) }}</span> 
     </div> 
    </div> 
    <div class="control-group"> 
     {{ form_label(form.image, 'Image', { 'attr': {'class': 'control-label'} }) }} 
     <div class="controls error"> 
      {{ form_widget(form.image, { 'attr': {'class': ''} }) }} 
      <span class="help-inline">{{ form_errors(form.image) }}</span> 
     </div> 
    </div> 
    {{ form_rest(form) }} 
    <div class="form-actions"> 
     <button type="submit" class="btn btn-primary">Enregistrer</button> 
     <a href="{{ path('parkingzone') }}" class="btn">Annuler</a> 
    </div> 
</form> 
+0

你知道在Symfony的单元测试和功能测试之间的区别,并且你已经有一个PHPUnit的测试成功提交表单的你的控制器的测试类扩展了Symfony WebTestCase? – redbirdo 2012-07-17 09:35:50

+0

我刚刚在底部回答了这个问题。请检查我的答案。 – 2012-07-17 13:18:03

+0

感谢您的额外信息。有两点 - 当控制器抛出异常时,用户在生产中看到的是“Oops,发生了错误”,这可以吗?其次,正如我在原始答案中试图解释的那样,您可以通过检查来自操作的响应中包含“无法找到ParkingZone实体”来进行测试,Symfony将显示包含异常消息的页面。我会用一个例子更新我的原始答案。 – redbirdo 2012-07-18 09:08:06

回答

2

Symfony本身没有任何对象,通过它可以操作表单的动作,因为它是在html(树枝文件)中设置的。但是,树枝提供了动态更改树枝文件中表单动作的功能。

基本的方法是控制器通过渲染调用将参数传递到树枝文件中。然后,该树枝文件可以使用此参数动态设置表单动作。如果控制器使用会话变量来确定此参数的值,那么通过在测试程序中设置此会话变量的值,可以专门为测试设置表单操作。

例如在控制器:

public function indexAction() 
{ 
    $session = $this->get('session'); 
    $formAction = $session->get('formAction'); 
    if (empty($formAction)) $formAction = '/my/normal/route'; 

    ... 

    return $this->render('MyBundle:XXX:index.html.twig', array(
     'form' => $form->createView(), 'formAction' => $formAction) 
    ); 
} 

然后,在树枝文件:

<form id="myForm" name="myForm" action="{{ formAction }}" method="post"> 
... 
</form> 

然后,在测试程序:

$client = static::createClient(); 
$session = $client->getContainer()->get('session'); 
$session->set('formAction', '/test/route'); 
$session->save(); 

// do the test 

这ISN”唯一的办法是,有各种可能性。例如,会话变量可以是$ testMode,如果设置了该变量,则表单将$ testMode = true传递给render调用。然后,根据testMode变量的值,树枝文件可以将表单动作设置为两个值之一。

+0

编辑添加丢失的$ session-> save()被@The Whole Life To Learn发现。 – redbirdo 2012-07-18 17:30:18

1

Symfony2中使得各个类别的单元测试和应用程序行为功能测试区别开来。单元测试是通过直接实例化一个类并调用它的方法来完成的。功能测试通过模拟请求和测试响应来执行。进一步的细节见symfony testing

表单提交只能在功能上进行测试,因为它由始终在容器环境中运行的Symfony控制器处理。 Symfony功能测试必须扩展WebTestCase类。这个类提供了访问用于请求URL,点击链接,选择按钮和提交表单的客户端。这些操作会返回一个表示HTML响应的搜寻器实例,用于验证响应是否包含预期内容。

只适用于测试在执行单元测试时抛出异常,因为功能测试涵盖了与用户的交互。用户不应该意识到抛出了异常。因此,最糟糕的情况是,Symfony捕捉到异常情况,并且在生产过程中向用户显示全部响应“糟糕,发生了错误”(或类似的定制消息)。但是,这应该只在应用程序中断时发生,而不是因为用户错误地使用了应用程序。因此,它通常不会在功能测试中进行测试。

关于问题中提到的第一个场景 - 提交带有错误参数的表单。在这种情况下,应向用户提供一条错误消息,告诉他们他们的输入有什么问题。理想情况下,控制器不应抛出异常,但应使用symfony validation来根据情况自动在每个字段旁边生成错误消息。无论显示错误的方式如何,都可以通过检查提交表单的响应是否包含预期错误来进行测试。例如:

class MyControllerTest extends WebTestCase 
{ 
    public function testCreateUserValidation() 
    { 
     $client = static::createClient(); 
     $crawler = $client->request('GET', '/new-user'); 
     $form = $crawler->selectButton('submit')->form(); 
     $crawler = $client->submit($form, array('name' => '', 'email' => 'xxx')); 
     $this->assertTrue($crawler->filter('html:contains("Name must not be blank")')->count() > 0, 
          "Page contains 'Name must not be blank'"); 
     $this->assertTrue($crawler->filter('html:contains("Invalid email address")')->count() > 0, 
          "Page contains 'Invalid email address'"); 
    } 
} 

关于第二种情况 - URL不完整。使用Symfony时,任何不匹配定义路由的URL都将导致NotFoundHttpException。在开发过程中,这会产生一条消息,如'找不到路由'GET/xxx''。在制作过程中,它会导致所有'糟糕,发生错误'。有可能在开发中测试响应中包含“找不到路由”。然而,在实践中,测试它是没有意义的,因为它是由Symfony框架处理的,因此是给定的。

编辑:

关于其中的URL包含识别对象的无效数据的情况。这可能(开发中)进行测试这样的单元测试程序:

$client = static::createClient(); 

$page = $client->request('GET', '/update/XXX'); 
$exceptionThrown = ($page->filter('html:contains("NotFoundException")')->count() > 0) && ($page->filter('html:contains("Unable to find ParkingZone entity")')->count() > 0); 
$this->assertTrue($exceptionThrown, "Exception thrown 'Unable to find ParkingZone entity'"); 

如果你只是想测试异常被抛出,而不是特定类型/消息你可以过滤HTML的“例外'。请记住,在制作过程中,用户只会看到“糟糕,发生了错误”,“例外”一词不会出现。

+0

我知道单元和功能测试之间的区别。我写了两种类型的测试。 我也定义了错误页面。 但是当用户修改操作链接(例如通过萤火虫)时会发生什么。例如/ film/4/edit成为/ film/abc/edit。 然后错误应该会上升,因为找不到电影实体。 – 2012-07-17 13:14:32

+0

之前的评论已被删除,因为我误读了您的示例 - 对不起! – redbirdo 2012-07-17 14:43:00

+0

在你给出的例子中,数据被作为URL的一部分传入。这与格式不正确的URL不同。任何以这种方式传递的值(或表单值)都应该经过明确的验证,并且将验证作为功能测试的一部分进行测试。如果某个特定的错误消息不是必需的或者不适用的话,那么可以抛出RuntimeException(的子类),这将导致Symfony在生产中出现catch-all错误。您的功能测试可以通过检查响应是否包含异常消息来验证开发中的这一点。 – redbirdo 2012-07-17 14:50:05

1

感谢@redbirdo和他的最后回答,我找到了一个解决方案,没有搞乱控制器。 我只改变了模板中的几行。

ControllerTest

public function testUpdate() 
{ 
    $client = static::createClient(); 
    $session = $client->getContainer()->get('session'); 
    $session->set('testActionForm', 'abc'); 
    $session->save();  // This line is important or you template won't see the variable 

    // ... tests 
} 

查看

{% if app.session.has('testActionForm') %} 
    {% set actionForm = path('parkingzone_update', { 'id': app.session.get('testActionForm') }) %} 
{% else %} 
    {% set actionForm = path('parkingzone_update', { 'id': entity.id }) %} 
{% endif %} 

<form action="{{ actionForm }}" {{ form_enctype(form) }} method="POST" class="form-horizontal"> 
// ... rest of the form 
+0

正如我的第二个答案提供了一个解决您的问题,我将不胜感激,如果你会接受它。这个问题没有具体说明答案应该避免对控制器进行更改,如果没有我给你的答案,你将无法产生这个答案。谢谢! – redbirdo 2012-07-18 12:20:35