└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # Goal 2 | Our goal is to get together Selenium best practices. Feedback is welcome. If you see anything wrong or know better way open an issue or create a pull request, just fork it and change it. We are open to learn and hear new ideas. 3 | 4 | # Selenium-best-practices 5 | Selenium is a portable software testing framework for web applications. It also provides a test domain-specific language to write tests in a number of popular programming languages, including Java, C#, Groovy, Perl, PHP, Python and Ruby. 6 | 7 | * [General](#general) 8 | * [Use PageObjects Pattern](#use-pageobjects-pattern) 9 | * [Prefered Selector Order](#prefered-selector-order) 10 | * [Create Ordered Tests](#create-ordered-tests) 11 | * [Get Name of the Running Test](#get-name-of-the-running-test) 12 | * [Do not Use Magic String](#do-not-use-magic-strings) 13 | * [Behavior Driven Design](#behavior-driven-design) 14 | * [Use Build Automation System](#use-build-automation-system) 15 | * [Learn Continuous Integration Tools](#learn-continuous-integration-tools) 16 | 17 | 18 | 19 | 20 | ## Use PageObjects Pattern 21 | 22 | Page Object is a Design Pattern which has become popular in test automation for enhancing test maintenance and reducing code duplication. An implementation of the page object model can be achieved by separating the abstraction of the test object and the test scripts. 23 | 24 | **Advantages of using Page Object Pattern:** 25 | * Easy to Maintain 26 | * Easy Readability of scripts 27 | * Reduce or Eliminate duplicacy 28 | * Re-usability of code 29 | * Reliability 30 | * There is a clean separation between test code and page specific code such as locators and layout. 31 | 32 | Use multi config for each environment. Make it changeable. Read your test case value from config. Don't use a static. 33 | 34 | * StageConfig 35 | * loginUrl=http://stage.com 36 | * validUsername=testUser@test.com 37 | * validPassword=123456 38 | 39 | * DevConfig 40 | * loginUrl=http://dev.com 41 | * validUsername=testUser@test.com 42 | * validPassword=123456 43 | 44 | **Shortcut** 45 | * Don't use magic strings. 46 | * Separate to WebPageElement, WebPageObject, WebTest. 47 | 48 | 49 | 50 | 51 | ## Prefered Selector Order 52 | 53 | Preferred selector order : id > name >links text> css > xpath 54 | 55 | CSS and XPath are location based selectors, they are slower than other selectors. 56 | 57 | * Id and name are often the easiest and sure way. 58 | * CSS = Id + name. 59 | * Last solution should be xpath. 60 | 61 | 62 | ## Create Ordered Tests 63 | 64 | ```java 65 | import org.junit.runners.MethodSorters; 66 | 67 | //Running tests in order of method names in ascending order 68 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 69 | //Running tests in order of method names in ascending order 70 | @FixMethodOrder(MethodSorters.JVM) 71 | ``` 72 | 73 | 74 | ## Get Name of the Running Test 75 | 76 | ```java 77 | import org.junit.rules.TestName; 78 | 79 | @Rule 80 | public TestName name = new TestName(); 81 | 82 | //example usage; writes every test name before it runs. 83 | @Before 84 | public void logBeforeTestsStart() { 85 | log.info(name.getMethodName()); 86 | } 87 | 88 | ``` 89 | 90 | 91 | ## Do not use Magic Strings 92 | 93 | It improves readability of the code and it's easier to maintain. 94 | 95 | ```java 96 | WebDriver driver = new HtmlUnitDriver(); 97 | // Don't use, create Constants each element name 98 | WebElement element = driver.findElement(By.name("sample")); 99 | driver.quit(); 100 | ``` 101 | 102 | 103 | ## Behavior Driven Design 104 | 105 | BDD is principally an idea about how software development should be managed by both business interests and technical insight, the practice of BDD assumes the use of specialized software tools to support the development process. 106 | Think all test scenarios. 107 | 108 | Example BDD 109 | 110 | ``` 111 | Feature: Sign up 112 | 113 | Sign up should be quick. 114 | Write all steps. 115 | 1- Open Browser 116 | 2-Navigate to Url 117 | 3-Click Sign Up Button 118 | 4-Write Valid Username and Password 119 | 5-Click Register. 120 | 121 | Scenario: Successful sign up 122 | New users should get a confirmation email and be greeted 123 | personally by the site once signed in. 124 | 125 | Given I have chosen to sign up 126 | When I sign up with valid details 127 | Then I should receive a confirmation email 128 | And I should see a personalized greeting message 129 | 130 | Scenario: Duplicate email 131 | 132 | Where someone tries to create an account for an email address 133 | that already exists. 134 | 135 | Given I have chosen to sign up 136 | But I enter an email address that has already registered 137 | Then I should be told that the email is already registered 138 | And I should be offered the option to recover my password 139 | 140 | ``` 141 | 142 | 143 | ###Simple Example 144 | 145 | 146 | ##Page Element Class 147 | ```java 148 | //Write clear class name. 149 | public class WebLoginPageElement { 150 | 151 | private String emailInputId; 152 | private String passwordInputId; 153 | public WebLoginPageElement() { 154 | this.emailInputId = "Email"; 155 | this.passwordInputId = "Password"; 156 | } 157 | 158 | public String getEmailInputId() { 159 | return emailInputId;} 160 | 161 | public String getPasswordInputId() { 162 | return passwordInputId;} 163 | 164 | ``` 165 | 166 | ##Page Object Class 167 | 168 | ```java 169 | public class WebLoginPage extends Function { 170 | 171 | //Make own custom WebElement Class. 172 | private WebElementFactory elementFactory; 173 | private IWebAction elementAction; 174 | private WebLoginPageElement loginPageElement; 175 | 176 | public WebLoginPage() { 177 | elementFactory = new WebElementFactory(driver); 178 | elementAction = new WebAction(driver); 179 | loginPageElement = new WebLoginPageElement(); 180 | } 181 | 182 | 183 | public WebLoginPage login(String email, String password) { 184 | 185 | WebElement emailInputElement = 186 | elementFactory.createElementById(loginPageElement.getEmailInputId()); 187 | elementAction.clear(emailInputElement); 188 | elementAction.sendKeys(emailInputElement, email); 189 | 190 | WebElement passwordInputElement = 191 | elementFactory.createElementById(loginPageElement.getPasswordInputId()); 192 | elementAction.clear(passwordInputElement); 193 | elementAction.sendKeys(passwordInputElement, password); 194 | 195 | WebElement loginSubmitButton = 196 | elementFactory.createElementByCssSelector(loginPageElement.getSubmitBtnCss()); 197 | elementAction.click(loginSubmitButton); 198 | return this;} 199 | ``` 200 | 201 | 202 | ##Test Class 203 | 204 | ```java 205 | 206 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 207 | public class WebLoginTest extends Function { 208 | 209 | private static org.apache.log4j.Logger log = Logger.getLogger(WebLoginTest.class); 210 | WebElementFactory elementFactory = new WebElementFactory(driver); 211 | private static WebLoginPageElement loginPageElement = new WebLoginPageElement(); 212 | 213 | @Rule 214 | public TestName name = new TestName(); 215 | 216 | @BeforeClass 217 | public static void setUpBeforeClass() throws Exception { 218 | PropertyConfigurator.configure(IConstants.LOG4j_PROP); 219 | openBrowser(RunAllWebTests.getConfigProperties().getLoginUrl()); 220 | } 221 | @AfterClass 222 | public static void tearDownAfterClass() throws Exception { 223 | driver.close(); 224 | driver.quit(); 225 | } 226 | 227 | //Test all scenarios. 228 | @Test 229 | public void loginWithEmptyUsername() throws Exception { 230 | //Call loginPage 231 | new WebLoginPage().login( 232 | RunAllWebTests.getConfigProperties().getEmptyUsername(), 233 | RunAllWebTests.getConfigProperties().getValidPassword()) 234 | .assertTruePageContain(loginPageElement.getEmptyUsernameAssert()); 235 | } 236 | 237 | @Test 238 | public void loginWithWrongFormat(){ 239 | //... 240 | } 241 | 242 | @Test 243 | public void loginWithWrongFormatId(){ 244 | //... 245 | } 246 | ``` 247 | 248 | --------------------------------------------------------------------------------