4

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

我浏览了 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>
4

3 回答 3

3

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

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

例如在控制器中:

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

这不是唯一的方法,还有多种可能性。例如,会话变量可以是 $testMode,如果设置了此变量,则表单将 $testMode = true 传递给渲染调用。然后,twig 文件可以根据 testMode 变量的值将表单操作设置为两个值之一。

于 2012-07-18T09:01:20.350 回答
1

Symfony2 区分了单个类的单元测试和应用程序行为的功能测试。单元测试是通过直接实例化一个类并在其上调用方法来进行的。通过模拟请求和测试响应来进行功能测试。更多细节参见symfony 测试

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

仅在执行单元测试时测试是否抛出异常是合适的,因为功能测试涵盖与用户的交互。用户永远不应该知道已经引发了异常。因此,最坏的情况是异常被 Symfony 捕获,并且在生产环境中,用户会看到包罗万象的响应“糟糕,发生了错误”(或类似的自定义消息)。但是,这应该只在应用程序损坏时才会发生,而不是因为用户错误地使用了应用程序。因此,通常不会在功能测试中对其进行测试。

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

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 以查找“异常”。请记住,在生产中,用户只会看到“糟糕,发生错误”,不会出现“异常”一词。

于 2012-07-17T10:45:35.640 回答
1

感谢@redbirdo 的最后回答,我找到了一个解决方案,而不会弄乱控制器。我只更改了模板中的几行。

控制器测试

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
于 2012-07-18T11:19:06.170 回答