These are the slides we presented at the 2009 Montreal CodeCamp for our FluentSelenium test DSL. FluentSelenium demonstrates how it is possible to make test code cleaner by introducing appropriate test abstractions.
see http://fluentselenium.codeplex.com/
7. Here's a web test written with selenium selenium.Open( "Forum/AllPosts.aspx" ); selenium.WaitForPageToLoad( "8000" ); selenium.Click( "newPost" ); selenium.WaitForPageToLoad( "8000" ); selenium.Type( "//*[contains(@id, 'txtAuthor')]" , "Luc" ); selenium.Type( "//*[contains(@id, 'txtSubject')]" , "Simple example of the library." ); selenium.Click( "//*[contains(@id, 'btnSavePost')]" ); selenium.WaitForPageToLoad( "8000" ); Assert .AreEqual( "Luc" , selenium.GetText( "//table[contains(@id, 'grdPosts')]//tr[2]/td[2]" ));
8. Xpath introduction XPath is a structred XML query language Example : get the node of the employee with ID 123456 Employees/Employee[@EmployeeID='123456'] < Employees > < Employee EmployeeID = " 123456 " > < LastName > Laporte </ LastName > < FirstName > Jacques </ FirstName > </ Employee > < Employee EmployeeID = " 654321 " > < LastName > Gendron </ LastName > < FirstName > Pierre </ FirstName > </ Employee > </ Employees >
9. Xpath introduction XPath is a structred XML query language Example : get the last name of the employee with ID 123456 : Employees/Employee[@EmployeeID='123456']/LastName < Employees > < Employee EmployeeID = " 123456 " > < LastName > Laporte </ LastName > < FirstName > Jacques </ FirstName > </ Employee > < Employee EmployeeID = " 654321 " > < LastName > Gendron </ LastName > < FirstName > Pierre </ FirstName > </ Employee > </ Employees >
10. Selenium and XPath on HTML Because HTML is an XML derivative, XPath can be used to locate nodes in a HTML web page Example : ask selenium to click on the Save button : selenium.Click(“//input[@id='btnSubmit']”); selenium.WaitForPageToLoad(“5000”); < html > < body > < form > < div >Hello World !</ div > < input type = " submit " value = " Save " id = " btnSubmit " /> </ form > </ body > </ html >
31. Tests are code also. Team code quality rules should also be applied to tests.
32. XPATH is dangerous, use it wisely Use ID-based queries when possible //*[@id = 'searchResultCount']
33. Avoid exessive XPATH steps When requiring further qualification in searches only add necessary qualifiers Favor //*[@id='searchResults']//li[1] Over /html/body//div[@id='searchResults']/ul/li[1]
34. XPATH queries are not clear Xpath expressions are the kind of literal who's meaning can be hard to decipher. Use constants. Favor selenium.GetText(SearchResultSummaryLocator); Over selenium.GetText( "//*[@id='searchResultsSummary']" );
47. Programmers tend to speak in terms of technical jargon In code this translates to the choice vocabulary used in class, method and variable names. Tests should use a DSL appropriate to the end user.
54. ... forumUser.Goto( "Forum/AllPosts.aspx" ); forumUser.For(addPostButton).Clicks(); forumUser.WaitFor(newPostPageLoadWait); forumUser.For(subjectTextBox).Enters( "Simple example of the library." ); forumUser.For(messagetTextBox).Enters( "blah" ); forumUser.For(savePostButton).Clicks(); forumUser.For(authorRequiredMessage).ShouldSee( "The author is required." );
55. The User The User class is the root of the DSL. All web test operations spawn from it. It is initialized as follows: var selenium = new DefaultSelenium (....); var forumUser = new User (selenium); Variables of type User should be named according to the role of the application user.
56. Pushing XPATH out of the tests FluentSelenium uses typed locator objects ElementLocator WelcomeMessageBox = ElementLocator .WithId( "welcome" ); InputLocator SearchCatalogText = InputLocator .WithId( "searchText" ); ButtonLocator SearchCatalogButton = ButtonLocator .WithId( "searchButton" ); ElementLocator FeaturedProductTitle = ElementLocator .WithXpath( "//*[@id='featuredProduct']//*[@id='title']" ); Locator types determine what operations are available with a “For” shopper.For(SearchCatalogText).Enters( "mouse" ); shopper.For(SearchCatalogButton).Clicks();
57. Application navigation Navigating to a specific address: goto shopper.Goto( "Shopping/ShopOnline.aspx" ); Navigating by clicking on links and buttons: click shopper.For(DiscountsButton).Clicks(); Waiting for page loading: WaitFor PageLoadWaitCondition SearchResultPageLoad = PageLoadWaitCondition .For( "Shopping/SearchResults.aspx" ); shopper.WaitFor(SearchResultPageLoad); Waiting for special conditions can be done by writing a custom implementation of IWaitCondition
58. Validating page content Presence or absence of an element shopper.ShouldSee(WelcomeMessageBox); shopper.ShouldNotSee(SearchResults); Content of an page element shopper.For(SearchResultsSummary).ShouldSee( "1 match found" ); Custom validation, any implementation of IContentValidator class AnyNumberValidator : IContentValidator { public void Validate( ContentTester contentTester) .... shopper.For(OrderNumber).ShouldSee(AnyNumber);
59. Testing table content Testing the content of a table row f orumUser.For(allPostsTable).Row(2).ShouldSee( "Luc" , "Hello" ); Jim Nothing Luc Hello Bob Goodbye Testing the content of a table column forumUser.For(allPostsTable).Column(1).ShouldSee( "Jim", "Luc", “Bob” ); Jim Nothing Luc Hello Bob Goodbye
60. Other table operations Checking cell content forumUser.For(allPostsTable).Row(2).Column(3).ShouldSee( "Present" ); Cells have content forumUser.For(allPostsTable).Row(2).Column(3).For(replyPostButton).Click();