Using Selenium2 with PHPUnit

Posted 2012-09-19
Written by Matt Frost
Category code

So I've taken it upon myself to do a bit of reading and messing around with the Selenium Extension for PHPUnit and I wanted to share a bit of what I learned. Looking around on the internet, I didn't find a whole lot of real good information on the Selenium2TestCase API so I want to provide a little bit of depth in some of those areas. I'm going to cover a few areas of interest and provide some code examples where relevant.

Getting set up

I have to admit, this is the one area where I probably didn't go as deep as I should have. I interacted with the the setBrowser and setBrowserUrl methods as a way to get started, but here's a little run down of what some of these methods look like.

setBrowser(String $browser) - Set the browser 
that Selenium will launch
setBrowserUrl(String $url) - Set the base 
URL for the tests, allows you to refer to relative paths in 
the tests
setHost(String $host) - Set the hostname 
for the connection of the Selenium Server
setPort(int $port) - Set the port for the 
connection to the Selenium Server
The host and port will default to your localhost and port 4444 if you don't explicitly assign them. Once you get those items configured for your situation, in the setUp method of course, you're ready to start writing your tests.

Selecting items

The coolest part of Selenium is that you can programmatically interact with a web browser, you get to tell it what to do and where to go. That being said, we first need to know how we're going to be selecting items. Most everyone is familiar with jQuery, so this concept shouldn't be new to many. There are a couple ways to select elements on the page that you're working on and here they are.

byId(String $id) - this will select 
the element that matches the id attribute
that you provide
byClassName(String $className) - this will
select the element the matches the class 
attribute you provide
byName(String $name) - this method matches
on the name attribute you provide
byXPath(String $xpath) - returns based on 
XPath pattern you provide
byCssSelector(String $selector) - returns based 
on the CSS Selector (uses # -for ids, . for classes, etc)
byLinkText(String $linkText) - not entirely 
sure, just stumbled upon this in the source, 
going to guess it matches link text
Using any of these selection methods, you should be able to select an element that you want to interact with; that's a really good start. How do we tell these elements what we want them to do though? Selecting them is all fine and well, but we want to start driving this boat!

Interacting with items

Now comes the fun stuff, here's where we specify the actions we want to perform on our items. I'm certainly not going to go through all the available options here, but I'm going to try to touch on some of the commonly used, or what I perceive to be commonly used, items and get you started there.

click() - performs a click
clear() - clears the value of the element
value() - returns the value of the element
value($value) - sets the value of the element to $value
text() - returns the text of the element
submit() - submits the element
Now I know to this point, I've really just pointed out a little bit of what is available to you. I think that's important, but at this time I'm going to take a bit of what we have here and show you how to use it a little bit. I'm not going to take the time to create an HTML page for this example, so let's just identify a few elements in plain english here; in fact let's use the example of a login screen. Our login screen has a text input for the username, a password input for the password, a button input to submit the form and let's assume that if the login fails the error "Invalid Login" is output to the screen. The ids we are going to be interacting with are username, password, submit and we're going to check the body the ensure that "Invalid Login" is not present. Here we go...
$username = $this->byId('username');
$password = $this->byId('password');
$submit = $this->byId('submit');
$body = $this->byCssSelector('body');

$username->value('test'); $password->value('password'); $submit->click(); $this->assertNotContains($body->text(),'Invalid Login');

Pretty cool right? We can chain those method calls together if we'd like, however if your going to use those elements again it's better to assign them. So what we've done is set the value of the username and password fields, clicked the submit button and the verified that the page that loaded after the form processed didn't contain the text 'Invalid Login'. That's a test you can run to make sure that valid users are allowed access and that failed attempts act as anticipated.

Getting around quicker

While it certainly is cool to drive around with a series of programmatic clicks, it can be tedious; so if you keep your tests focused on a single area of your application you can get to the starting point of your test by using the url() method. It took me a while to find this, because SeleniumTestCase has an open(String $url) method that makes more sense to me from a naming standpoint. url() will navigate your browser to the url that you provide, which means you don't have to spend a ton of time faux-clicking through your navigation menus and things of that nature.

Conclusion

Hopefully you found this somewhat helpful, please know that there are other options available for interacting with Selenium. WebDriver is a common one, perhaps when I get a chance to look into it more closely, I'll be able to write a post similar to this one. As always, I'm interested to hear your comments, concerns and corrections that you may have for this post. Thanks for checking it out!

Fin

Comments

Gravatar
Mindyk

2012-09-19

Nice post (- :
setting the browser in the setup method is valid if you want to test only one browser or write seperate tests for every browser you want to test.

One topic i had some trouble with is testing ajax stuff. (for example the login form would load via ajax) i ended up using this method: git://gist.github.com/3749188.git

Also links to selenium2 and the full api would be nice as you mention more then once that you are talking only about a subset of the api

regards, michael

Gravatar
Giorgio Sironi

2012-09-21

I'm the maintainer and current main developer of the extension, thanks for the post. The api you're using here *is* the WebDriver one, which has been merged with Selenium2 during development.
So that's why open() has been changed to url(): to conform to the new API (google for JSON Wire Protocol).

If you want to contribute to either the documentation or the code, you're welcome to open pull requests on github. :)

Posting comments after has been disabled.