Monday, October 30, 2023
Saturday, July 22, 2023
An Introduction to mind maps for testers
Why?
- Similar to identifying test conditions and done as part of the test analysis activity.
- Better medium to convey the test scope to other stakeholders than written test conditions.
- As relationships between branches and a complete picture can be seen, it can be a better guide than test conditions when test design is done.
How?
- Think in terms of categories, where supersets should be closely related than subsets.
- Think in terms of use case actors.
- Think in terms of quality characteristics
- Think in terms of architecture
- In terms of success and failure interactions.
- In terms of design techniques
- Classification tree
- Write in short form
- Level of detail should be decided on project requirements and constraints. If you have broken it down to the level of high-level test cases you have gone too far.
Sunday, April 16, 2023
Multi Tab Support in Selenium Web Driver
Selenium Web Driver is capable of handling multiple browser tabs. Though the requirement to automate workflows that span multiple tabs is one that automation engineers don’t come across all the time, it’s always good to know how it can be done.
package com.dumiduh.other; | |
import com.dumiduh.constants.Constants; | |
import com.dumiduh.utils.TestBase; | |
import org.openqa.selenium.By; | |
import org.testng.annotations.AfterClass; | |
import org.testng.annotations.BeforeClass; | |
import org.testng.annotations.Test; | |
import java.util.List; | |
import java.util.stream.Collectors; | |
public class MultiTabTest extends TestBase { | |
@BeforeClass | |
public static void setup() { | |
instantiateDriver(); | |
driver.get(Constants.MULTI_WINDOW); | |
} | |
@Test | |
public static void multiWindowTest() { | |
System.out.println("URL:\t" + driver.getCurrentUrl()); | |
//opening a new tab | |
driver.findElement(By.xpath("//*[text()='Click Here']")) | |
.click(); | |
//A new tab has been opened. | |
System.out.println("Number of tabs\t" + driver.getWindowHandles() | |
.size()); | |
List<String> windows = driver.getWindowHandles() | |
.stream() | |
.map(window -> { | |
return window; | |
}) | |
.collect(Collectors.toList()); | |
driver.switchTo() | |
.window(windows.get(windows.size() - 1)); | |
driver.get("https://www.yahoo.com"); | |
System.out.println("URL:\t" + driver.getCurrentUrl()); | |
} | |
@AfterClass | |
public static void cleanUp() { | |
driver.quit(); | |
} | |
} |
Saturday, April 1, 2023
Selenium Custom Locators
package com.dumiduh.utils; | |
import org.openqa.selenium.By; | |
import org.openqa.selenium.SearchContext; | |
import org.openqa.selenium.WebElement; | |
import java.util.List; | |
public class Text extends By { | |
private String searchTerm; | |
private final String XPATH = "//*[contains(text(),'%s')]"; | |
public static By containsText(String searchTerm) { | |
return new Text(searchTerm); | |
} | |
private Text(String value) { | |
this.searchTerm = value; | |
} | |
@Override | |
public List<WebElement> findElements(SearchContext context) { | |
return context.findElements(By.xpath(String.format(XPATH, searchTerm))); | |
} | |
} |
package com.dumiduh.other; | |
import com.dumiduh.constants.Constants; | |
import com.dumiduh.utils.Text; | |
import com.dumiduh.utils.TestBase; | |
import org.testng.Assert; | |
import org.testng.annotations.AfterClass; | |
import org.testng.annotations.BeforeClass; | |
import org.testng.annotations.Test; | |
/*** | |
* The objective of this test is to demonstrate the use of a custom locator. | |
*/ | |
public class CustomeLocatorTest extends TestBase { | |
@BeforeClass | |
public static void setup() { | |
instantiateDriver(); | |
driver.get(Constants.DYNAMIC_LOAD); | |
} | |
@Test | |
public static void byContainsText() { | |
Assert.assertTrue(driver.findElement(Text.containsText("Powered by")).isDisplayed(), "the expected text value was not found in the html document."); | |
} | |
@AfterClass | |
public static void cleanUp() { | |
driver.quit(); | |
} | |
} |
Friday, March 10, 2023
A Comparison of Network Interception capabilities between Cypress and Selenium
To evaluate this capability I went about automating the 3 scenarios found below,
- Intercepting and waiting for a network resource attached to a web page to be received to the client side.
- Intercepting and simulating 400 and 500 errors.
- Intercepting and changing response payloads.
/// <reference types='cypress'/> | |
describe("Intercepting network resources", ()=>{ | |
it("Intercepting and waiting for a URL", ()=>{ | |
cy.intercept({ | |
method: "GET", | |
url: "https://the-internet.herokuapp.com/css/app.css" | |
}).as("slowResource") | |
cy.visit("https://the-internet.herokuapp.com/dropdown"); | |
cy.wait("@slowResource").its('response.statusCode').should('eq', 200); | |
}) | |
it("Intercepting and throwing a network error for a resource", ()=> { | |
cy.intercept({ | |
method: "GET", | |
url: "https://the-internet.herokuapp.com/dropdown" | |
},{ | |
forceNetworkError: true | |
}).as("slowResource") | |
cy.visit("https://the-internet.herokuapp.com/dropdown"); | |
}) | |
it.skip("Intercepting and loging the request object", ()=>{ | |
cy.intercept({ | |
method: "GET", | |
url: "https://the-internet.herokuapp.com/dropdown" | |
}, (req) => { | |
console.log(req); | |
}).as("slowResource") | |
cy.visit("https://the-internet.herokuapp.com/dropdown"); | |
cy.wait("@slowResource").its('response.statusCode'); | |
}) | |
it("Intercepting and replying with a static response", ()=>{ | |
cy.intercept('GET','https://the-internet.herokuapp.com/dropdown', {statusCode: 200, body: "<html><body>test</body></html>"}).as("slowResource") | |
cy.visit("https://the-internet.herokuapp.com/dropdown"); | |
cy.wait("@slowResource").its('response.statusCode').should('eq', 200); | |
}) | |
}) |
package com.dumiduh.other; | |
import io.github.bonigarcia.wdm.WebDriverManager; | |
import org.openqa.selenium.chrome.ChromeDriver; | |
import org.openqa.selenium.devtools.NetworkInterceptor; | |
import org.openqa.selenium.remote.http.Contents; | |
import org.openqa.selenium.remote.http.HttpResponse; | |
import org.openqa.selenium.remote.http.Route; | |
import org.testng.annotations.AfterClass; | |
import org.testng.annotations.BeforeClass; | |
import org.testng.annotations.Test; | |
import static org.openqa.selenium.remote.http.HttpMethod.GET; | |
public class NetworkInterceptorTest { | |
ChromeDriver driver; | |
@BeforeClass | |
public void setup(){ | |
WebDriverManager.chromedriver().setup(); | |
} | |
@Test | |
public void responseInterceptionError() { | |
driver = new ChromeDriver(); | |
Route route = Route.matching(req -> GET == req.getMethod() && req.getUri() | |
.endsWith("/dropdown")) | |
.to(() -> req -> new HttpResponse().setStatus(500)); | |
new NetworkInterceptor(driver, route); | |
driver.get("https://the-internet.herokuapp.com/dropdown"); | |
driver.getPageSource() | |
.contains("HTTP ERROR 500"); | |
} | |
@Test | |
public void responseInterceptionBody() { | |
driver = new ChromeDriver(); | |
Route route = Route.matching(req -> GET == req.getMethod() && req.getUri() | |
.endsWith("/dropdown")) | |
.to(() -> req -> new HttpResponse().setContent(Contents.utf8String("<html><body>test</body></html>"))); | |
new NetworkInterceptor(driver, route); | |
driver.get("https://the-internet.herokuapp.com/dropdown"); | |
} | |
@AfterClass | |
public void cleanup(){ | |
driver.quit(); | |
} | |
} |
Saturday, February 25, 2023
Abstracting the Test Layer using TestNG
Leaving abstraction out of the test layer can in time lead to duplication of the logic as well as having to spend more time than what is required to script new scenarios. This post will discuss how the test layer can be abstracted and decomposed using TestNG.
High-level Steps
- Start with designing and implementing test cases so that they are atomic
- Encapsulate the test layer by scripting the test steps and adding assertions required to validate the component. Then put a mechanism in place to read the test data from an object that can be passed from one test class to another as required.
- Finally, use the Factory feature in TestNG to compose the larger scripts using the test classes.
- Validate that the dropdown page loads the dropdown and that the values are as expected.
- Validate that the dropdown allows the user to select the option he wants to select.
Test Class 1
package com.dumiduh.elements; | |
import com.dumiduh.constants.Constants; | |
import com.dumiduh.function.DropDownPageFunctions; | |
import com.dumiduh.models.TestData; | |
import com.dumiduh.utils.JSONUtil; | |
import com.dumiduh.utils.TestBase; | |
import org.testng.Assert; | |
import org.testng.annotations.AfterClass; | |
import org.testng.annotations.BeforeClass; | |
import org.testng.annotations.Test; | |
/** | |
* The objective of this test class is to demonstrate how a dropdown maybe handled. | |
*/ | |
public class DropDownUsageTest extends TestBase { | |
@BeforeClass | |
public static void setup() { | |
instantiateDriver(); | |
driver.get(Constants.DROPDOWN_PAGE_URL); | |
} | |
@Test | |
public static void dropDownUsageTest() { | |
TestData testData = DropDownTest.data; | |
System.out.println("::\t"+ testData.getDropDownSelection()); | |
DropDownPageFunctions dropDownPageFunctions = new DropDownPageFunctions(driver); | |
dropDownPageFunctions.selectValueFromDropDown(testData.getDropDownSelection()); | |
//Asserts to see if the dropdown selection has been set. | |
Assert.assertTrue(dropDownPageFunctions.isTheGivenValueSelected(testData.getDropDownSelection())); | |
} | |
@AfterClass | |
public static void cleanUp(){ | |
driver.quit(); | |
} | |
} |
Test Class 2
package com.dumiduh.elements; | |
import com.dumiduh.function.DropDownPageFunctions; | |
import com.dumiduh.models.TestData; | |
import com.dumiduh.utils.JSONUtil; | |
import com.dumiduh.utils.TestBase; | |
import org.testng.Assert; | |
import org.testng.annotations.AfterClass; | |
import org.testng.annotations.BeforeClass; | |
import org.testng.annotations.Test; | |
import static com.dumiduh.constants.Constants.DROPDOWN_PAGE_URL; | |
public class DropDownPageElementsTest extends TestBase { | |
@BeforeClass | |
public static void setup() { | |
instantiateDriver(); | |
} | |
@Test | |
public static void dropDownPageElementTest() { | |
TestData data = DropDownTest.data; | |
driver.get(DROPDOWN_PAGE_URL); | |
DropDownPageFunctions dropdown = new DropDownPageFunctions(driver); | |
Assert.assertTrue(dropdown.isTheDropDownHeadingDisplayed()); | |
Assert.assertTrue(dropdown.isTheDropDownDisplayed()); | |
Assert.assertEquals(data.getNumberOfOptions(), dropdown.getTheListOfOptions() | |
.size()); | |
if (dropdown.getTheListOfOptions() | |
.containsAll(data.getListOfOptions())) { | |
Assert.assertTrue(true); | |
} | |
} | |
@AfterClass | |
public void cleanUp() { | |
DropDownTest.data.setDropDownSelection("Option 2"); | |
driver.quit(); | |
} | |
} |
Factory Class
package com.dumiduh.elements; | |
import com.dumiduh.models.TestData; | |
import com.dumiduh.utils.JSONUtil; | |
import org.testng.annotations.Factory; | |
public class DropDownTest { | |
static TestData data = JSONUtil.readAGivenTestDataItem("dropdownendtoend"); | |
@Factory | |
public static Object[] DropDownTest() { | |
return new Object[]{ | |
new DropDownPageElementsTest(), | |
new DropDownUsageTest()}; | |
} | |
} |
[1] - https://the-internet.herokuapp.com/dropdown
[repo] - https://github.com/handakumbura/SeleniumAutomationEmployerProfile/tree/feature/atomic_test_cases
Monday, January 30, 2023
Portability testing a web application UI with Selenium
Portability testing of web application UI can easily be handled with Selenium. To handle cross browser testing you can implement a driver instantiation pattern like the one shown below,
public class TestBase { | |
protected static WebDriver driver; | |
public static void instantiateDriver() { | |
if (BROWSER.equals("chrome")) { | |
WebDriverManager.chromedriver().setup(); | |
ChromeOptions options = new ChromeOptions(); | |
driver = new ChromeDriver(options); | |
} else if (BROWSER.equals("edge")) { | |
WebDriverManager.edgedriver().setup(); | |
EdgeOptions options = new EdgeOptions(); | |
options.addArguments("start-maximized"); | |
driver = new EdgeDriver(options); | |
} else{ | |
Logger.getAnonymousLogger().log(Level.INFO,"An unsupported browser argument was provided at run-time."); | |
} | |
} | |
} |
For cross mobile agent testing, you can use a TestNG data provider to pass in the device types into your test method and repeat the test for as many emulated devices as you would like.
Find the complete project here, https://github.com/handakumbura/SeleniumAutomationEmployerProfile/tree/feature/cross_browser_cross_agent
public class CrossMobileAgentTest extends TestBase { | |
@AfterMethod | |
public static void cleanUp() { | |
driver.quit(); | |
} | |
@Test(dataProvider = "userAgentsData") | |
public static void testDataProvider(String s) { | |
ChromeOptions options = new ChromeOptions(); | |
options.setExperimentalOption("mobileEmulation", Map.of("deviceName", s)); | |
driver = new ChromeDriver(options); | |
driver.get(Constants.DROPDOWN_PAGE_URL); | |
DropDownPageFunctions dropDownPageFunctions = new DropDownPageFunctions(driver); | |
TestData testData = JSONUtil.readTestData("002"); | |
dropDownPageFunctions.selectValueFromDropDown(testData.getStringValue()); | |
//Asserts to see if the dropdown selection has been set. | |
Assert.assertTrue(dropDownPageFunctions.isTheGivenValueSelected(testData.getStringValue()), "The given value was not set as the dropdown selection."); | |
} | |
@DataProvider(name = "userAgentsData") | |
public static Object[][] generateData() { | |
return new String[][]{{"iPhone 5"}, {"iPhone 6"}, {"Nexus 6"}}; | |
} | |
} |
Saturday, January 14, 2023
How to read the user selection from a radio button group using Selenium?
SeleniumUtil library has been occupying my free time for the past couple of months. Since I’ve already written at length about what it is I won’t do that again here but if you really want to know you look here [1] [2].
When I implemented the abstraction wrapper for the HTML radio button group I ran into a problem. How would someone go about reading the user selection form a radio button group? Take a look at this HTML block,
// store the value of the radio button that the user selects by attaching event handlers to the radio group members. | |
window.radio; document.querySelectorAll('[name="age"]').forEach(elem => {elem.addEventListener('click',function(e){ window.radio = e.target.defaultValue})}); | |
// read the stored value later when the user has made the selection. | |
return window.radio; |
Saturday, January 7, 2023
SeleniumUtil 0.7.0 is now available with a few Java Script utility methods
Abstraction helps to promote code reuse and reduce development time among other benefits. When considering Selenium based test automation, you can abstract at many different points such as at dependency, infrastructure, technology, page, component or even at the element or locator level. SeleniumUtil is an open source library that aims to provide generic abstractions and other useful utilities so that you don’t need to worry about them in your Selenium based test automation projects.
Version 0.7.0 is now available in maven central and it provides a few technology level abstractions in the form of a few parameterized Java Script methods.
Usage
package com.dumiduh.other; | |
import com.dumiduh.constants.Constants; | |
import com.dumiduh.utils.TestBase; | |
import io.github.handakumbura.EventListener; | |
import io.github.handakumbura.JavaScriptHelper; | |
import org.testng.annotations.AfterClass; | |
import org.testng.annotations.BeforeClass; | |
import org.testng.annotations.Test; | |
public class JavaScriptHelper extends TestBase { | |
private static final String OPTIONS_BLOCK = "'<option>papaya</option><option>apple</option>'"; | |
private static final String DROPDOWN_CSS_SELECTOR = "#dropdown"; | |
private static final String CALLBACK = "function(){console.log('clicked')}"; | |
@BeforeClass | |
public static void setup() { | |
instantiateDriver(); | |
driver.get(Constants.DROPDOWN_PAGE_URL); | |
} | |
@Test | |
public static void dropDownTest() { | |
JavaScriptHelper javaScriptHelper = new JavaScriptHelper(driver); | |
//pauses execution up to 2 minutes. | |
javaScriptHelper.pauseTheDOMForAGivenDuration(5000); | |
//attaches a valid and well-formed HTML block as a child of a given element. | |
javaScriptHelper.appendHTMLBlockAsAChild(OPTIONS_BLOCK,DROPDOWN_CSS_SELECTOR); | |
//attaches a callback function to a given event in the DOM. | |
javaScriptHelper.attachASnippetAsAEventCallBack(DROPDOWN_CSS_SELECTOR, EventListener.CLICK,CALLBACK); | |
} | |
@AfterClass | |
public static void cleanUp() { | |
driver.quit(); | |
} | |
} |
-
Why? Similar to identifying test conditions and done as part of the test analysis activity. Better medium to convey the test scope to other ...
-
BASH[1], to my understanding is not meant to be used as a general purpose programming language and attempting to use it for such purposes ca...
-
Learning SQE is like learning to play the drums, anyone can learn to keep a beat, but very few can master the instrument. SQE is hard becaus...