logo
Ask your Symfony questions! Pay money and get answers fast! (more info)

Warning: Please do not give out any FTP or ssh credentials to anyone, unless you trust them completely. Giving out login details is dangerous.

If the asker does not get an answer then they have 10 days to request a refund.

$15
How to loop over every link on a page in functional tests?

In a functional test, is it possible to get all the links on the page, and then loop over them? I'd like to click on each link and look for 404 errors. In pseudo code, I'd like something like this:

  $browser->get('/login')->
click('.signin_login', array('signin' => array('username' => $username, 'password' => $password)))->
with('form')->
hasErrors(false)->
with('user')->begin()->
isAuthenticated(true)->
hasCredential(array('tastingnotes.user'))->
end()->
get('restaurant_award/index')->
click('a', array(), array('position' => $browser->incrementPosition('a')))->
with('response')->begin()->
isStatusCode(200)->
end()
;


What I'm not clear how to do is the magic incrementPosition() method.

I could do something very ugly, like use debug() to get the HTML and then use regex to count the number of links and then go into a loop and in the loop call all of the above code, incrementing a counter that is handled by the loop. But I'd like to believe there is a more graceful way to handle this.

Lawrence Krubner | 03/22/11 at 12:48pm Edit
Tutorial: How to assign prize money


(9) Responses

See a threaded view of answers?

Warning: Please do not give out any FTP or ssh credentials to anyone, unless you trust them completely. Giving out login details is dangerous.

  • avatar
    Last edited:
    03/23/11
    1:07pm
    Jakub Zalas says:

    You can quite easily implement your own testers:


    class zTesterResponse extends sfTesterResponse
    {
    /**
    * @return sfTestFunctionalBase|sfTester
    */
    public function assertNo404Links()
    {
    // do magic things here
    // ...
    // you can use $this->browser->getResponse() or $this->response

    return $this->getObjectToReturn();
    }
    }


    And than use them:


    $browser = new sfTestFunctional(new sfBrowser());
    $browser->setTester('response', 'zTesterResponse');

    $browser->
    [...]
    with('response')->assertNo404s()


    If you extend sfTesterResponse you'l have content in the $this->response property. You can also access it with $this->browser->getResponse();

    I'm not sure however, if it's a good practice to manipulate browser in your tester (getters are fine).

    Maybe it's better to extend sfTestFunctional and add your method there. If you decide to extend sfTestFunctional you'll be also able to access your browser with protected $this->browser property.


  • avatar
    Last edited:
    03/23/11
    3:59pm
    Lawrence Krubner says:

    But how do I reference links on a page via sfBrowser? That is what I am asking. I'd like to click() each link, test for 404 (or for 200) then hit the back() method, then click() the next link.

  • avatar
    Last edited:
    03/23/11
    6:47pm
    Jakub Zalas says:

    Didn't test it but code below should do the job:


    class zTestFunctional extends sfTestFuncional
    {
    public function assertNo404s()
    {
    $nodes = $this->browser->getResponseDomCssSelector()->matchAll('a')->getNodes();
    foreach ($nodes as $node)
    {
    $this->click($node);
    $this->with('response')->isStatusCode(200);
    $this->back();
    }

    return $this;
    }
    }


    Example usage:


    $browser->get('/login')->
    click('.signin_login', array('signin' => array('username' => $username, 'password' => $password)))->
    with('form')->
    hasErrors(false)->
    with('user')->begin()->
    isAuthenticated(true)->
    hasCredential(array('tastingnotes.user'))->
    end()->
    get('restaurant_award/index')->
    assertNo404s()
    ;

  • avatar
    Last edited:
    03/23/11
    6:49pm
    Jakub Zalas says:

    Of course you should use zTestFunctional instead of sfTestFunctional.

  • avatar
    Last edited:
    03/25/11
    11:41am
    Lawrence Krubner says:

    Hmm, might work. Except now, using your code, I get "functional no controller found".

    I used "php -l" on all the files to be sure there were no syntax errors anywhere.

    I'm calling:

    ./symfony test:functional tastingnotes home_page_test


    and:

      $browser->get('/login')->                                                                              
    click('.signin_login', array('signin' => array('username' => $username, 'password' => $password)))->
    with('form')->
    hasErrors(false)->
    with('user')->begin()->
    isAuthenticated(true)->
    hasCredential(array('tastingnotes.user'))->
    end()->
    get('wine/index')->
    assertNo404s()
    end()
    ;


  • avatar
    Last edited:
    03/25/11
    11:50am
    Lawrence Krubner says:

    Strange. If I run:

    ./symfony test:functional tastingnotes


    The other 23 files run, and their tests are ok, but the home_page_test never runs. I thought maybe this was a naming convention, so changed the file name to homePageTest.php and ran :

    ./symfony test:functional tastingnotes homePageTest

    but I still get:


    functional no controller found

  • avatar
    Last edited:
    03/25/11
    11:55am
    Lawrence Krubner says:

    Changed the code to:

    class msTestFunctional extends sfTestFuncional
    {

    public function assertNo404s()
    {
    $nodes = $this->browser->getResponseDomCssSelector()->matchAll('a')->getNodes();

    print_r($nodes);

    foreach ($nodes as $node)
    {
    $this->click($node);
    $this->with('response')->isStatusCode(200);
    $this->back();
    }

    return $this;
    }


    but I never get to the print_r statement.


    Test file is:


    <?php                                                                                                    

    include(dirname(__FILE__).'/../../bootstrap/functional.php');

    $browser = new msTestFunctional(new sfBrowser());

    $username = 'xxxx';
    $password = 'xxxx';

    $browser->get('/login')->
    click('.signin_login', array('signin' => array('username' => $username, 'password' => $password)))->
    with('form')->
    hasErrors(false)->
    with('user')->begin()->
    isAuthenticated(true)->
    hasCredential(array('tastingnotes.user'))->
    end()->
    get('wine/index')->
    assertNo404s()
    end()
    ;

  • avatar
    Last edited:
    03/25/11
    12:05pm
    Lawrence Krubner says:

    Okay, that was a naming convention, regarding the test file. I figured it out and your code works great. Thanks.

    Sad to say I get "Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 311296 bytes) in /home/dev/tastingnotes/cache/tastingnotes/test/config/config_routing.yml.php on line 208
    "

    I'll try to figure out a workaround for that.

  • avatar
    Last edited:
    03/25/11
    12:06pm
    Jakub Zalas says:

    Where's your msTestFunctional class located? Is it autoloaded? If not did you require it with require or require_once?

    In jobeet there's an example of creating custom functional test class: http://www.symfony-project.org/jobeet/1_2/Propel/en/09#chapter_09_test_data

    I think you're close.

    While running a test you shouldn't include a "Test" word. It should be:


    ./symfony test:functional tastingnotes homePage

This question has expired.





Current status of this question: Community pot



Please log in to add additional discourse to this page.





Warning: Please do not give out any FTP or ssh credentials to anyone, unless you trust them completely. Giving out login details is dangerous.

If the asker does not get an answer then they have 10 days to request a refund.