Chapter 3 SiteTest example

Hi,

These two tests fail even though when I manually go to the page and click submit the corresponding error messages display as they should. I even tried copy pasting the error message straight from the source of the web page into the assert statement to make sure it’s the same. I notice the javascript has the error message in double quotes but can’t find for the life of me where in the application that text is to see if I can change it to single quotes to see if that would make a difference.

$this->assertTextPresent(‘Body cannot be blank.’);

$this->assertTextPresent(‘Password cannot be blank.’);

Thanks

Hi,

I suspect this is happening because the test script is running faster than the ajax callback. You need to wait for the the ajax call to complete. Here is how you can fix it. for Yii 1.1.8.





public function testContact()

	{

		$this->open('?r=site/contact');

		$this->assertTextPresent('Contact Us');

		$this->assertElementPresent('name=ContactForm[name]');


		$this->type('name=ContactForm[name]','tester');

		$this->type('name=ContactForm[email]','tester@example.com');

		$this->type('name=ContactForm[subject]','test subject');

		$this->click("//input[@value='Submit']");

		

	 for ($second = 0; ; $second++) {

    	if ($second >= 60) $this->fail("timeout");

    	try {

        	if ($this->isElementPresent("id=ContactForm_body_em_")) break;

    	} catch (Exception $e) {}

    	sleep(1);

	}

		

		//$this->assertTextPresent('Body cannot be blank.');

	}




if you are looking for the actual error message, I believe that it is going to be injected into the html tag in the view.

HTH.

like it? +1 me!;)

Thanks for the fast reply Windsor.

I couldn’t reply back last night because of the 3 post limit for new users.

I tried your suggestion which was good by the way and something I have saved for future use but it didn’t resolve this issue.

I should clarify that the message I get from phpunit is “Failed asserting that <boolean:false> is true.” not “body cannot be blank” so I think it’s more that just a fail.

I could not find assertTextPresent() in the documentation class reference but instead found ElementContainsText(locator, string, message). I found the third parameter and how to use it at both phpunit and selinium sites but it doesn’t work.

I can’t get that to work because it can’t seem to find the locator. I’ve tried id, class, div, form, etc for the locator but always get something to the affect of <string:> not found. I saw an example somewhere and it should be saying <string:{locator}> with locator being an id or name so that tells me something is not working with right with it.

Oh and the javascript I was asking about so I can see the text is in the framework/validators/CRequiredValidator.php.

It says ‘{attribute} cannot be blank.’ and what ever the attribute is gets put in the place holder and pushed out to the javascript somehow. But that’s not really part of the issue. Just cool to know where it is if I want to change the text.

I learned something about Gii too. Aparently as I went forward throught the book and made more models and controllers it recreated the siteTest and phpunit.xml so I had to go change them again to get back the point of the error. Guess I’ll have to look closer at the list of files to be generated and uncheck those if they are there.

Any other suggestions?

Thanks

Here are the tests. The issue is with line 21 and 36 which I have commented out at the moment.




<?php


class SiteTest extends WebTestCase

{

	public function testIndex()

	{

		$this->open('');

		$this->assertTextPresent('Welcome');

	}


	public function testContact()

	{

		$this->open('?r=site/contact');

		$this->assertTextPresent('Contact Us');

		$this->assertElementPresent('name=ContactForm[name]');

		$this->type('name=ContactForm[name]','tester');

		$this->type('name=ContactForm[email]','tester@example.com');

		$this->type('name=ContactForm[subject]','test subject');

		$this->click("//input[@value='Submit']");

		

    	//$this->assertTextPresent('Body cannot be blank.');

	}

		

	public function testLoginLogout()

	{

		$this->open('');

		// ensure the user is logged out

		if($this->isTextPresent('Logout'))

			$this->clickAndWait('link=Logout (demo)');


		// test login process, including validation

		$this->clickAndWait('link=Login');

		$this->assertElementPresent('name=LoginForm[username]');

		$this->type('name=LoginForm[username]','demo');

		$this->click("//input[@value='Login']");

		//$this->assertTextPresent('Password cannot be blank.');

		$this->type('name=LoginForm[password]','demo');

		$this->clickAndWait("//input[@value='Login']");

		$this->assertTextNotPresent('Password cannot be blank.');

		$this->assertTextPresent('Logout');


		// test logout process

		$this->assertTextNotPresent('Login');

		$this->clickAndWait('link=Logout (demo)');

		$this->assertTextPresent('Login');

	}

}



I’m attaching the screeshot.

I tried your suggested timer again and this time tested with taking out the break and with setting the time up to 10 seconds so I can see selinium working and it sits there and still doesnt’ find those two. Strange thing is that it finds the text for the other tests in SiteTest.php

Oh and I noticed there was no exception being thrown to catch and the sleep was not inside the catch block so I did some modifying and came up with this.




		for ($second = 0; ; $second++) {

            if ($second >= 60) $this->fail("timeout");

                try {

                        if (!$this->isElementPresent("id=ContactForm_body_em_"))

                        {

                             throw Exception(); 

                        }

                        else{

                            break;

                        }


                    } catch (Exception $e)

                         {

                            sleep(10);

                         }

        

        }



It still doesn’t fix the problem but appears to be working as a timer becuase it actually times out after 10 seconds of trying. It was getting ignored the other way.

I tried it on my system running windows 7, xampp 1,7.7, Yii 1.1.8, PHP 5.3.8, Selenium 2.8.0

(Reproduce your problem perfectly, on a slightly different website.)

Here is the error:

This test fails

here is the test script in its entirety that generates this error:




<?php


class SiteTest extends WebTestCase

{

	public function testIndex()

	{

		$this->open('');

		$this->assertTextPresent('Cases');

	}


	public function testContact()

	{

		$this->open('?r=site/contact');

		$this->assertTextPresent('Contact Us');

		$this->assertElementPresent('name=ContactForm[name]');


		$this->type('name=ContactForm[name]','tester');

		$this->type('name=ContactForm[email]','tester@example.com');

		$this->type('name=ContactForm[subject]','test subject');

		$this->click("//input[@value='Submit']");

		

 	for ($second = 0; ; $second++) {

    	if ($second >= 60) $this->fail("timeout");

    	try {

        	if ($this->isElementPresent("id=ContactForm_body_em_")) break;

    	} catch (Exception $e) {}

    	sleep(1);

	}

		

		//$this->assertTextPresent('Body cannot be blank.');

	}


	public function testLoginLogout()

	{

		$this->open('');

		// ensure the user is logged out

		if($this->isTextPresent('Logout'))

			$this->clickAndWait('link=Logout (demo)');


		// test login process, including validation

		$this->clickAndWait('link=Login');

		$this->assertElementPresent('name=LoginForm[username]');

		$this->type('name=LoginForm[username]','Test_User_Two');

		$this->click("//input[@value='Login']");

		/*

		for ($second = 0; ; $second++) {

			if ($second >= 60) $this->fail("timeout");

			try {

				if ($this->isElementPresent("id=LoginForm_password_em_")) break;

			} catch (Exception $e) {

			}

			sleep(1);

		}

		*/

		$this->assertTextPresent('Password cannot be blank.');

		

		$this->type('name=LoginForm[password]','test2');

		$this->clickAndWait("//input[@value='Login']");

		$this->assertTextNotPresent('Password cannot be blank.');

		$this->assertTextPresent('Logout');


		// test logout process

		$this->assertTextNotPresent('Login');

		$this->clickAndWait('link=Logout (Test_User_Two)');

		$this->assertTextPresent('Login');

	}

}







now I uncomment the delay code and comment the offending statement in testLoginLogout() and make no other changes and it works ! !





for ($second = 0; ; $second++) {

			if ($second >= 60) $this->fail("timeout");

			try {

				if ($this->isElementPresent("id=LoginForm_password_em_")) break;

			} catch (Exception $e) {

			}

			sleep(1);

		}

		

		//$this->assertTextPresent('Password cannot be blank.');




This Works! Thmbnail

So I’m thinking that code as written is working properly…

Well I wasn’t sure about having a catch without a throw so that was confusing me but I see that you were correct in having the sleep() function outside the catch brackets. Don’t know what I was thinking there.

I just realized that the element "id=ContactForm_body_em_" is always present because it is hard coded so it should break from the loop regardless of whether the text "Body cannot be blank." exists. If I navigate to the contact form and view source without submitting this is already there.


<div class="errorMessage" id="ContactForm_body_em_" style="display:none"></div>

So, the element is found and it breaks from the loop immediately. The element exists but the text doesn’t.

I tried replacing the check for the element with the check for the text and it just sits there going through the loop never finding the text.

I think it is some issue with javascript because if I disable javascript, submit the form and then view source I see this


<div class="errorMessage" id="ContactForm_body_em_">Body cannot be blank.</div>

But with javascript enabled I see this


<div class="errorMessage" id="ContactForm_body_em_" style="display:none"></div>

Update: I found another post

http://www.yiiframework.com/forum/index.php?/topic/17981-selenium-tests-fail-in-new-117-webapp/

It was definitely caused by javascript validation. Confusing that it’s still a problem and the javascript seems to be still working when disabled client side but ok, sleep() fixes the issue. I just didn’t need to loop and check for isElementPresent().

A simple sleep before each failing assertion fixes it.


public function testContact()

	{

		$this->open('?r=site/contact');

		$this->assertTextPresent('Contact Us');

		$this->assertElementPresent('name=ContactForm[name]');

		$this->assertElementPresent('id=ContactForm_body_em_');

		$this->type('name=ContactForm[name]','tester');

		$this->type('name=ContactForm[email]','tester@example.com');

		$this->type('name=ContactForm[subject]','test subject');

		$this->click("//input[@value='Submit']");

        sleep(1);

        $this->assertTextPresent('Body cannot be blank.');

	}

		

	public function testLoginLogout()

	{

		$this->open('');

		// ensure the user is logged out

		if($this->isTextPresent('Logout'))

			$this->clickAndWait('link=Logout (demo)');


		// test login process, including validation

		$this->clickAndWait('link=Login');

		$this->assertElementPresent('name=LoginForm[username]');

		$this->type('name=LoginForm[username]','demo');

		$this->click("//input[@value='Login']");

		sleep(1);

		$this->assertTextPresent('Password cannot be blank.');

		$this->type('name=LoginForm[password]','demo');

		$this->clickAndWait("//input[@value='Login']");

		$this->assertTextNotPresent('Password cannot be blank.');

		$this->assertTextPresent('Logout');


		// test logout process

		$this->assertTextNotPresent('Login');

		$this->clickAndWait('link=Logout (demo)');

		$this->assertTextPresent('Login');

	}

}

I guess it pretty evident of the problem now, that all the other assertTextPresent tests that pass are checking for hard coded text rather than javascript text and that’s why they pass.

Thank you Windsor for pointing out the main trouble maker(javascript) and the sleep() fix even if I didn’t need the loop part.

I faced the same error when do the phpunit test on functional/SiteTest.php. Since it’s the timing of the the code that caused it, I took a slightly different modification of the SiteTest.php code. Simply add a sleep(2) works:

	&#036;this-&gt;click(&quot;//input[@value='Submit']&quot;);


            sleep(2);


	&#036;this-&gt;assertTextPresent('Body cannot be blank.');

	&#036;this-&gt;assertElementPresent('name=LoginForm[username]');


	&#036;this-&gt;type('name=LoginForm[username]','demo');


	&#036;this-&gt;click(&quot;//input[@value='Login']&quot;);


            sleep(2);


	&#036;this-&gt;assertTextPresent('Password cannot be blank.');

Hey guys!

I am a newbie so please excuse my naivety.

I was running the SiteTest.php test as well and had the same problem. I discovered that the actual web page displays "Logout(demo)" and hence I changed the assertion for this string. It failed with the exact message that you have been analysing. I checked the source of the web page and found that the string sent to the browser was "Logout (demo)", i.e., it had a space between the "t" in Logout and "(". Even though the rendered page looks like it has no spaces.

I simply changed the test to:

$this->clickAndWait(‘link=Logout (demo)’);

and the test worked fine.

Hope this helps.

brettm