In this post, I will show some basic usage of Splinter for simple web application tests. Splinter is a tool useful on tests of any web application. You can even test a Java web application using Splinter. This post example is a "test" of a Facebook feature, just because I want to focus on how to use Splinter, not on how to write a web application. The feature to be tested is the creation of an event (the Splinter sprint), following all the flow: first the user will login on Facebook, then click on "Events" menu item, then click on "Create an Event" button, enter all event informations and click on "Create event" button. So, let’s do it…
First step is create a Browser instance, which will provide method for interactions with browser (where the browser is: Firefox, Chrome, etc.). The code we need for it is very simple:
browser = Browser("firefox")
Browseris a class and its constructor receives the driver to be used with that instance. Nowadays, there are three drivers for Splinter: firefox, chrome and zope.testbrowser. We are using Firefox, and you can easily use Chrome by simply changing the driver from
chrome. It’s also very simple to add another driver to Splinter, and I plan to cover how to do that in another blog post here.
A new browser session is started when we got the
browserobject, and this is the object used for Firefox interactions. Let's start a new event on Facebook, the Splinter Sprint. First of all, we need to visit the Facebook homepage. There is a
visitmethod on Browser class, so we can use it:
visitis a blocking operation: it waits for page to load, then we can navigate, click on links, fill forms, etc. Now we have Facebook homepage opened on browser, and you probably know that we need to login on Facebook page, but what if we are already logged in? So, let's create a method that login on Facebook with provided authentication data only the user is not logged in (imagine we are on a TestCase class):
def do_login_if_need(self, username, password): if self.browser.is_element_present_by_css('div.menu_login_container'): self.browser.fill('email', username) self.browser.fill('pass', password) self.browser.find_by_css('div.menu_login_container input[type="submit"]').first.click() assert self.browser.is_element_present_by_css('li#navAccount')What was made here? First of all, the method checks if there is an element present on the page, using a CSS selector. It checks for a
divthat contains the username and password fields. If that div is present, we tell the browser object to fill those fields, then find the
submitbutton and click on it. The last line is an assert to guarantee that the login was successful and the current page is the Facebook homepage (by checking the presence of “Account”
We could also find elements by its texts, labels or whatever appears on screen, but remember: Facebook is an internationalized web application, and we can’t test it using only a specific language.
Okay, now we know how to visit a webpage, check if an element is present, fill a form and click on a button. We're also logged in on Facebook and can finally go ahead create the Splinter sprint event. So, here is the event creation flow, for a user:
- On Facebook homepage, click on “Events” link, of left menu
- The “Events” page will load, so click on “Create an Event” button
- The user see a page with a form to create an event
- Fill the date and chose the time
- Define what is the name of the event, where it will happen and write a short description for it
- Invite some guests
- Upload a picture for the event
- Click on “Create Event” button
findthe link and
find_by_cssmethod takes a CSS selector and returns an ElementList. So, we get the first element of the list (even when the selector returns only an element, the return type is still a list) and click on it. Like
clickis a blocking operation: the driver will only listen for new actions when the request is finished (the page is loaded).
We’re finally on "new event" page, and there is a form on screen waiting for data of the Splinter Sprint. Let’s fill the form. Here is the code for it:
browser.fill('event_startIntlDisplay', '5/21/2011') browser.select('start_time_min', '480') browser.fill('name', 'Splinter sprint') browser.fill('location', 'Rio de Janeiro, Brazil') browser.fill('desc', 'For more info, check out the #cobratem channel on freenode!')That is it: the event is going to happen on May 21th 2011, at 8:00 in the morning (480 minutes). As we know, the event name is Splinter sprint, and we are going to join some guys down here in Brazil. We filled out the form using
fillmethod is used to fill a "fillable" field (a textarea, an input, etc.). It receives two strings: the first is the name of the field to fill and the second is the value that will fill the field.
selectis used to select an option in a select element (a “combo box”). It also receives two string parameters: the first is the name of the select element, and the second is the value of the option being selected.
Imagine you have the following select element:
<select name="gender"> <option value="m">Male</option> <option value="f">Female</option> </select>To select “Male”, you would call the select method this way:
browser.select("gender", "m")The last action before click on “Create Event” button is upload a picture for the event. On new event page, Facebook loads the file field for picture uploading inside an
iframe, so we need to switch to this frame and interact with the form present inside the frame. To show the frame, we need to click on “Add Event Photo” button and then switch to it, we already know how click on a link:
browser.find_by_css('div.eventEditUpload a.uiButton').first.click()When we click this link, Facebook makes an asynchronous request, which means the driver does not stay blocked waiting the end of the request, so if we try to interact with the frame BEFORE it appears, we will get an
ElementDoesNotExistexception. Splinter provides the
is_element_presentmethod that receives an argument called
wait_time, which is the time Splinter will wait for the element to appear on the screen. If the element does not appear on screen, we can’t go on, so we can assume the test failed (remember we are testing a Facebook feature):
if not browser.is_element_present_by_css('iframe#upload_pic_frame', wait_time=10): fail("The upload pic iframe did'n't appear :(")The
is_element_present_by_cssmethod takes a CSS selector and tries to find an element using it. It also receives a
wait_timeparameter that indicates a time out for the search of the element. So, if the
iframeelement with ID=”upload_pic_frame” is not present or doesn’t appear in the screen after 10 seconds, the method returns
False, otherwise it returns
Important:Now we see the
failis a pseudocode sample and doesn’t exist (if you’re using
unittestlibrary, you can invoke
self.failin a TestCase, exactly what I did in complete snippet for this example, available at Github).
iframeelement on screen and we can finally upload the picture. Imagine we have a variable that contains the path of the picture (and not a file object,
StringIO, or something like this), and this variable name is
picture_path, this is the code we need:
with browser.get_iframe('upload_pic_frame') as frame: frame.attach_file('pic', picture_path) time.sleep(10)Splinter provides the
get_iframemethod that changes the context and returns another objet to interact with the content of the frame. So we call the
attach_filemethod, who also receives two strings: the first is the name of the input element and the second is the absolute path to the file being sent. Facebook also uploads the picture asynchronously, but there’s no way to wait some element to appear on screen, so I just put Python to sleep 10 seconds on last line.
After finish all these steps, we can finally click on “Create Event” button and asserts that Facebook created it:
browser.find_by_css('label.uiButton input[type="submit"]').first.click() title = browser.find_by_css('h1 span').first.text assert title == 'Splinter sprint'After create an event, Facebook redirects the browser to the event page, so we can check if it really happened by asserting the header of the page. That’s what the code above does: in the new event page, it click on submit button, and after the redirect, get the text of a span element and asserts that this text equals to “Splinter sprint”.
That is it! This post was an overview on Splinter API. Check out the complete snippet, written as a test case and also check out Splinter repository at Github.