2012-07-17 35 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