| Smoke Testing | Sanity Testing |
| Used for check local critical functionalities of a software | Focus will be on a particular area or minor functionalities |
| Performed to check stability | Used to verify the rationality |
| Both manual and automation test cases can be used | Generally, sanity testing does not have test scripts or test cases |
| Usually performed before passing the build to testing team | Executed before UAT and regression |
| Carried over by developers | Performed by testers |
| Subset of acceptance testing | Subset of regression testing |
Google Analytics 4 vs Universal Analytics
In late 2020 Google released their latest iteration of Analytics, Google Analytics 4. The release was met with some controversy as changes to the interface, verbiage, reporting, and conversions left users wondering if they would still have access to relevant data tracking they had set up in Universal Analytics. In this blog post we will discuss some of the biggest differences between Google Analytics 4 and Universal Analytics, and how you can use Google Analytics 4 to its fullest potential.
Data Streams vs. Views
In Universal Analytics you could create custom “Views” to remove certain data from reporting, provide limited access to team members, or monitor specific pages on your website.
In GA4 these customization are referred to as “Data Streams,” and offer more flexibility, especially if you are monitoring websites and mobile applications. A data stream is classified as a flow of data from a customer touchpoint to Analytics. Each GA4 property can have up to 50 data streams monitoring any combination of web or app data, with a maximum of 30 app data streams. To create a custom data stream click the “Admin” gear on the left navigation bar, click “Data Streams” and then “Add Data Stream.” You will then be given the option to select an IOS App, Android App, or web property.

GA4 Interface & Verbiage Changes
If you’re opening up Google Analytics 4 for the first time you are probably going to notice that it looks very different. They have made some significant changes to the interface and verbiage across GA4 and Universal Analytics that may make it difficult for you to navigate at first. As we made the transition we wrote down some of the major changes, how to navigate them, and how to get the most relevant data out of GA4.
The main navigation bar on the left side of your Analytics interface got a huge makeover, moving many of your favorite data collection tools to new areas, and renaming many of them as well. You will also notice some changes in how the data is presented. Google has implemented a system of cards that organizes data collection into more specific categories, and allows you to get a more in depth look by clicking these cards. Below we have outlined some changes to the major areas of influence between Universal and GA4, and how to navigate through the new changes.
Acquisition
Acquisition moved down a few spaces in GA4, but is still easy to navigate to. The real changes come when you click to reveal the dropdown. At first glance it appears that you have less data options to examine, but by clicking “Overview” you will notice that you can customize your data in ways that weren’t possible with Universal Analytics.
On the acquisition overview page you will notice that there are cards running the acquisition data against metrics like session medium, user medium, and a card dedicated to your PPC campaigns if you are running any. While the data provided on the cards may seem underwhelming, you are provided access to a multitude of data when you dig deeper by clicking the cards.

User Acquisition- User acquisition shows the channels that new users use to find your website, and will only include data for new users. Each channel can be broken down for further analysis.
Traffic Acquisition- Traffic Acquisition shows the channels associated with each session across all site visits. Again, each channel can be broken down for further analysis.
Conversions
Conversions are user activities that contribute to the success of your business, and look very different in GA4. In Universal Analytics these were referred to as “Goals,” but in GA4 we call them “Conversion Events.”
When you set up your GA4 property Google automatically assigns a list of events to track on your website.

You can mark any of these automatically assigned events as conversions, and you can also add up to 30 conversion events of your own per property. You may notice that some things have been added to the conversion list that you aren’t used to seeing there. In GA4 Google automatically recognizes things like page views and clicks as a conversion event.
The handy table below we pulled from Google’s support page outlines how GA4’s property measurement differs from Universal analytics. GA4’s property measurement is event based, with the viewpoint that any interaction can be captured as an event. This is why you are seeing things like clicks and page views in the conversion section of GA4.

Reports
The way that Google handles reporting is similar in GA4, but they have made some key changes that will affect what you are analyzing and how you do it. Like we mentioned earlier, Google uses cards to consolidate information so you can quickly analyze large amounts of data. The cards generally include one metric and one dimension, and address a simple question or metric of your business. When you click on these cards you are taken to a more comprehensive overview, offering you the opportunity to include a broader set of metrics in your report.

While this report seems familiar, you are automatically given access to new metrics, and metrics that weren’t generally available in Universal Analytics.
This new card style reporting is especially helpful when you are analyzing things like demographics, where there are many different metrics that you will find useful. When clicking the demographics tab on the left nav bar you are taken to the page below, giving you immediate access to data that would have required multiple clicks in Universal Analytics.

Engagement
Engagement is a section new to GA4, and offers some really hopeful data to ensure your content is relevant to your audience. As you may have seen in the reports, GA4 features a new metric, engaged sessions.
Engaged Sessions
A new metric to GA4 is Engaged Sessions. To be classified as an engaged session the user’s session must be at least 10 seconds long, contain a conversion event, or had two or more page views. This opens up all kinds of new metrics like Engaged Sessions Per User, Average Engagement Time per Session, and Engagement rate. This is one of our favorite additions to GA4, as it offers us quick data that tells us how relevant our content is.
Explore Reports
Analysis Hub
The Analysis Hub is a new term for Analytics, but it serves a familiar purpose. Think of the Analysis Hub as the replacement for the Customization dashboard in Universal Analytics. This hub allows you to create and store all of your custom reports for easy access and sharing. Creating these custom reports also gives you the opportunity to run path analyses
Exploration
Exploration Reports is a highly flexible technique that gives you the ability to organize your data with customized graphs and charts. This can be especially helpful if you are sharing this information with members of your team.
Funnel Analysis
The funnel analysis in GA4 allows you to analyze the journey your users go on as they navigate your site. There are multiple variations of Funnel Analysis that you can use to get insights from different starting points.
Open Funnel- users can enter the funnel in any step
Closed Funnel- users must enter the funnel in the first step.
Path Analysis
Path Analysis is similar to the funnel analysis, but they are visualized with a tree graph. This section allows you to see pages users view and other actions users may take as they engage with your content. The data presented in Path Analysis can be filtered and segmented to narrow the focus of your analysis.
Segment Overlap
Segment Overlap allows you to create up to 3 user segments and view how those segments overlap and relate with each other. Segments can include age, city, events completed, new users, and more. By isolating different audiences, you are able to discover new insights and apply them to other areas of your analysis.
Realtime Analysis
The Realtime Analysis has received a huge update in GA4 and is much more intuitive. The Realtime Overview shows you data on your users in the last 30 minutes, and can be a great snapshot of your current users.

Retention
Another new tab in the navigation bar of GA4 is the Retention tab. This gives you an insight into how well your website or app is retaining visitors. When you click the “Overview” link, you will see data cards running data against “cohorts.”

What is Cohort? – a cohort is a group of users who share a common characteristic that is identified by an Analytics dimension. For example, all users who came to your website via organic search belong to the same cohort.
These reports are helpful to measure the success of one-time marketing campaigns or events, and to measure how new users are interacting with your site over specific date periods. You can then use this data to fine-tune your marketing efforts during different seasons or Holidays.
E-Commerce
The E-commerce functions in GA4 look very different than they did in Universal and can be found via their own tab in the navigation bar called “Monetization.” Like we stated earlier, one of the drawbacks in GA4 is it’s lack of E-commerce support. With its own slot in the navigation bar we hope that in the coming months Google will unveil a robust E-commerce monitoring system, including the ability to monitor in-app purchases.

While they have begun light support of E-Commerce, you may not have access to the sophisticated metrics you had set up in Universal Analytics. This setup process involves lot’s of complex code additions to your website.
Conclusion
With Google Analytics 4 Google has opened up the door for some very powerful data collection tools. While they still have some work to do to make GA4 as effective for E-Commerce companies, implementing GA4 in conjunction with your Universal Analytics can unlock some powerful data so you can better serve your audience.
How to Share Access to Google Analytics: Step-By-Step Guide
Sharing Google Analytics Access is a must as you are working with your team to make the most out of your data. We have shared a simple step-by-step guide below that showcases how to share access to your Universal and Google Analytics 4 Accounts.
Universal Analytics
Step 1
Click the “Admin” button in the bottom right corner.

Step 2
Click “Account User Management.”
Note: You can also do this at the Property and View levels if you don’t want the user to have access to every property or view in the account.

Step 3
Click the blue + to add a new user to your account, property, or view.

Step 4
Enter the email of the person you want to share the account with, and select their level of permissions by checking one of the boxes. You can apply them singly, or select multiple options based on your user needs. We have outlined these permissions below so you have a good understanding of what you are providing access to.
Google has a great rundown of these permissions on their website, we would suggest checking these out so you are certain your users will have enough, or not too much access.

A Note on Google Analytics 4
To share access to your Google Analytics 4 Account follow the same steps above. The only difference you will notice is the option to share “view” access is no longer available in GA4 because they removed “views” from the interface. To learn more about Google Analytics 4 and the changes to expect check out our post Google Analytics 4 vs Universal Analytics.
VS Code C# – System.NotSupportedException: No data is available for encoding 1252
If you are moving from the .Net framework to .Net core, you may come across many issues.
Especially if you are reading something from excel you may get below issue somewhere around the process.
“No data is available for encoding 1252. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method.”
This happens due to the encoding difference in .Net core.
This issue can be easily be fixed by adding the below line of code in your excel handling methods.
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
Don’t forget to add the corresponding NuGet package also.
So while excel reading may look like this.
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
FileStream stream = File.Open(ExcelRepFile, FileMode.Open, FileAccess.Read);
IExcelDataReader excelReader = null;
try
{
if (ExcelRepFile.EndsWith(".xls"))
{
excelReader = ExcelReaderFactory.CreateBinaryReader(stream);
}
if (ExcelRepFile.EndsWith(".xlsx"))
{
excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
}
}
catch (Exception)
{
throw;
}
Thanks for reading How to fix “No data is available for encoding 1252” error with ExcelReaderFactory in .Net Core
Data driven testing in C# with NUnit and RestSharp
For those of you that do not know what I mean with ‘data driven’: when I want to run tests that exercise the same logic or flow in my application under test multiple times with various combinations of input values and corresponding expected outcomes, I call that data driven testing.
This is especially useful when testing RESTful APIs, since these are all about sending and receiving data as well as exposing business logic to other layers in an application architecture (such as a graphical user interface) or to other applications (consumers of the API).
As a starting point, consider these three tests, written using RestSharp and NUnit:
[TestFixture]
public class NonDataDrivenTests
{
private const string BASE_URL = "http://api.zippopotam.us";
[Test]
public void RetrieveDataForUs90210_ShouldYieldBeverlyHills()
{
// arrange
RestClient client = new RestClient(BASE_URL);
RestRequest request =
new RestRequest("us/90210", Method.GET);
// act
IRestResponse response = client.Execute(request);
LocationResponse locationResponse =
new JsonDeserializer().
Deserialize<LocationResponse>(response);
// assert
Assert.That(
locationResponse.Places[0].PlaceName,
Is.EqualTo("Beverly Hills")
);
}
[Test]
public void RetrieveDataForUs12345_ShouldYieldSchenectady()
{
// arrange
RestClient client = new RestClient(BASE_URL);
RestRequest request =
new RestRequest("us/12345", Method.GET);
// act
IRestResponse response = client.Execute(request);
LocationResponse locationResponse =
new JsonDeserializer().
Deserialize<LocationResponse>(response);
// assert
Assert.That(
locationResponse.Places[0].PlaceName,
Is.EqualTo("Schenectady")
);
}
[Test]
public void RetrieveDataForCaY1A_ShouldYieldWhiteHorse()
{
// arrange
RestClient client = new RestClient(BASE_URL);
RestRequest request =
new RestRequest("ca/Y1A", Method.GET);
// act
IRestResponse response = client.Execute(request);
LocationResponse locationResponse =
new JsonDeserializer().
Deserialize<LocationResponse>(response);
// assert
Assert.That(
locationResponse.Places[0].PlaceName,
Is.EqualTo("Whitehorse")
);
}
}
Please note that the LocationResponse type is a custom type I defined myself, see the GitHub repository for this post for its implementation.
These tests are a good example of what I wrote about earlier: I’m invoking the same logic (retrieving location data based on a country and zip code and then verifiying the corresponding place name from the API response) three times with different sets of test data.
This quickly gets very inefficient when you add more tests / more test data combinations, resulting in a lot of duplicated code. Luckily, NUnit provides several ways to make these tests data driven. Let’s look at two of them in some more detail.
Using the [TestCase] attribute
The first way to create data driven tests is by using the [TestCase] attribute that NUnit provides. You can add multiple [TestCase] attributes for a single test method, and specify the combinations of input and expected output parameters that the test method should take.
Additionally, you can specify other characteristics for the individual test cases. One of the most useful ones is the TestName property, which can be used to provide a legible and useful name for the individual test case. This name also turns up in the reporting, so I highly advise you to take the effort to specify one.
Here’s what our code looks like when we refactor it to use the [TestCase] attribute:
[TestFixture]
public class DataDrivenUsingAttributesTests
{
private const string BASE_URL = "http://api.zippopotam.us";
[TestCase("us", "90210", "Beverly Hills", TestName = "Check that US zipcode 90210 yields Beverly Hills")]
[TestCase("us", "12345", "Schenectady", TestName = "Check that US zipcode 12345 yields Schenectady")]
[TestCase("ca", "Y1A", "Whitehorse", TestName = "Check that CA zipcode Y1A yields Whitehorse")]
public void RetrieveDataFor_ShouldYield
(string countryCode, string zipCode, string expectedPlaceName)
{
// arrange
RestClient client = new RestClient(BASE_URL);
RestRequest request =
new RestRequest($"{countryCode}/{zipCode}", Method.GET);
// act
IRestResponse response = client.Execute(request);
LocationResponse locationResponse =
new JsonDeserializer().
Deserialize<LocationResponse>(response);
// assert
Assert.That(
locationResponse.Places[0].PlaceName,
Is.EqualTo(expectedPlaceName)
);
}
}
Much better! We now only have to define our test logic once, and NUnit takes care of iterating over the values defined in the [TestCase] attributes:

There are some downsides to using the [TestCase] attributes, though:
- It’s all good when you just want to run a small amount of test iterations, but when you want to / have to test for larger numbers of combinations of input and output parameters, your code quickly gets messy (on a side note, if this is the case for you, try looking into property-based testing instead of the example-based testing we’re doing here).
- You still have to hard code your test data in your code, which might give problems with scaling and maintaining your tests in the future.
This is where the [TestCaseSource] attribute comes in.
Using the [TestCaseSource] attribute
If you want to or need to work with larger numbers of combinations of test data and/or you want to be able to specify your test data outside of your test class, then using [TestCaseSource] might be a useful option to explore.
In this approach, you specify or read the test data in a separate method, which is then passed to the original test method. NUnit will take care of iterating over the different combinations of test data returned by the method that delivers the test data.
Here’s an example of how to apply [TestCaseSource] to our tests:
[TestFixture]
public class DataDrivenUsingTestCaseSourceTests
{
private const string BASE_URL = "http://api.zippopotam.us";
[Test, TestCaseSource("LocationTestData")]
public void RetrieveDataFor_ShouldYield
(string countryCode, string zipCode, string expectedPlaceName)
{
// arrange
RestClient client = new RestClient(BASE_URL);
RestRequest request =
new RestRequest($"{countryCode}/{zipCode}", Method.GET);
// act
IRestResponse response = client.Execute(request);
LocationResponse locationResponse =
new JsonDeserializer().
Deserialize<LocationResponse>(response);
// assert
Assert.That(
locationResponse.Places[0].PlaceName,
Is.EqualTo(expectedPlaceName)
);
}
private static IEnumerable<TestCaseData> LocationTestData()
{
yield return new TestCaseData("us", "90210", "Beverly Hills").
SetName("Check that US zipcode 90210 yields Beverly Hills");
yield return new TestCaseData("us", "12345", "Schenectady").
SetName("Check that US zipcode 12345 yields Schenectady");
yield return new TestCaseData("ca", "Y1A", "Whitehorse").
SetName("Check that CA zipcode Y1A yields Whitehorse");
}
}
In this example, we specify our test data in a separate method LocationTestData(), and then tell the test method to use that method as the test data source using the [TestDataSource] attribute, which takes as its argument the name of the test data method.
For clarity, the test data is still hard coded in the body of the LocationTestData() method, but that’s not mandatory. You could just as easily write a method that reads the test data from any external source, as long as the test data method is static and returns an object of type IEnumerable, or any object that implements this interface.
Also, since the [TestCase] and [TestCaseSource] attributes are features of NUnit, and not of RestSharp, you can apply the principles illustrated in this post to other types of tests just as well.
Beware, though, before you use them for user interface-driven testing with tools like Selenium WebDriver. Chances are that you’re falling for a classic case of ‘just because you can, doesn’t mean you should’. I find data driven testing with Selenium WebDriver to be a test code smell: if you’re going through the same screen flow multiple times, and the only variation is in the test data, there’s a high chance that there’s a more efficient way to test the same underlying business logic (for example by leveraging APIs).
Selenium C# Tutorial: Handling Multiple Browser Tabs
The concept of Window Handle remains the same whether a URL is opened in a new window or a new tab. For demonstrating how to handle multiple browser tabs in Selenium C#, we use the test URL as http://the-internet.herokuapp.com/windows The Click Here link on the test page is located using the XPath property. In our earlier articles, we covered XPath in Selenium in greater detail.

Once the button is clicked, the URL http://the-internet.herokuapp.com/windows/new opens up in a new browser tab. The window handle count becomes two with driver.WindowHandles[0] representing window handle of ‘parent window’ and driver.WindowHandles[1] representing window handle of ‘child window’.
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Support.UI;
using SeleniumExtras.WaitHelpers;
using System;
using System.Collections.ObjectModel;
namespace Selenium_Window_Demo_1
{
class Selenium_Window_Demo_1
{
IWebDriver driver;
[SetUp]
public void start_Browser()
{
// Local Selenium WebDriver
driver = new ChromeDriver();
driver.Manage().Window.Maximize();
}
[Test, Order(1)]
public void test_window_ops()
{
String test_url = "http://the-internet.herokuapp.com/windows";
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
driver.Url = test_url;
var browser_button = driver.FindElement(By.XPath("//a[.='Click Here']"));
Assert.AreEqual(1, driver.WindowHandles.Count);
string WindowTitle = "The Internet";
Assert.AreEqual(WindowTitle, driver.Title);
browser_button.Click();
Assert.AreEqual(2, driver.WindowHandles.Count);
Console.WriteLine("Window Handle[0] - " + driver.WindowHandles[0]);
var newTabHandle = driver.WindowHandles[1];
Assert.IsTrue(!string.IsNullOrEmpty(newTabHandle));
Console.WriteLine("Window Handle[1] - " + driver.WindowHandles[1]);
Assert.AreEqual(driver.SwitchTo().Window(newTabHandle).Url, "http://the-internet.herokuapp.com/windows/new");
string expectedNewWindowTitle = "New Window";
Assert.AreEqual(driver.SwitchTo().Window(newTabHandle).Title, expectedNewWindowTitle);
/* Thread Sleep is not a good practice since it is a blocking call */
/* Only used for demonstration */
System.Threading.Thread.Sleep(2000);
driver.SwitchTo().Window(driver.WindowHandles[1]).Close();
driver.SwitchTo().Window(driver.WindowHandles[0]);
System.Threading.Thread.Sleep(2000);
}
[TearDown]
public void close_Browser()
{
driver.Quit();
}
}
}
The SwitchTo().Window(driver.WindowHandles[0]) is used to switch back to the parent window after closing the child window.
var newTabHandle = driver.WindowHandles[1];
Assert.IsTrue(!string.IsNullOrEmpty(newTabHandle));
Assert.AreEqual(driver.SwitchTo().Window(newTabHandle).Url, "http://the-internet.herokuapp.com/windows/new");
string expectedNewWindowTitle = "New Window";
Assert.AreEqual(driver.SwitchTo().Window(newTabHandle).Title, expectedNewWindowTitle);
/* Thread Sleep is not a good practice since it is a blocking call */
/* Only used for demonstration */
System.Threading.Thread.Sleep(2000);
driver.SwitchTo().Window(driver.WindowHandles[1]).Close();
driver.SwitchTo().Window(driver.WindowHandles[0]);
Here are the Window Handles of the two browser windows that were instantiated during automated browser testing:

Selenium WebDriver C# – get text
Below is the example of Html element on the page:
<li id="city" class="anketa_list-item">
<div class="anketa_item-city">From</div>
London
</li>
If you want only London by WebDriver?
You could easily get by using class-name
driver.FindElement(By.Class("anketa_item-city")).Text;
or using Xpath
driver.FindElement(By.Xpath("\li\div")).Text;
Get Text Of An Element In Selenium?
Selenium is the most widely used automation testing tool, which reduces human effort and efficiently handles testing the scenarios we encounter every day. One such scenario is how to get the text of an element in Selenium. Selenium offers a getText() method used to get the text of an element, i.e.; it can be used to read text values of an element from a web page.
What Is getText() Method?
The Selenium WebDriver interface has predefined the getText() method, which helps retrieve the text for a specific web element. getText() method in Selenium fetches the inner text of an element, which is not hidden by CSS and returns it as a String value. In simple words, whatever is displayed as text on the browser will be returned as-is by the getText() method. If there is no text corresponding to a web element an empty string is returned.
Let us now begin with the practical application of the getText() method.
Using getText() Method To Get Heading Or Paragraph
We all know that no website is complete without headings or paragraph content. At times it becomes necessary to verify the text on the web page we are landing on. The getText() method in Selenium helps us retrieve a text and do necessary action on it. In the code below, we are reading the heading text from the web page, comparing it with the expected value, and then printing the results.
package getText;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
public class TextThruHeading {
WebDriver driver;
@BeforeTest
public void setUp(){
System.setProperty("webdriver.chrome.driver","E:\\Softwares\\chromedriver.exe");
driver = new ChromeDriver();
}
@Test
public void headingText(){
driver.get("https://phptravels.com/demo/");
driver.manage().window().maximize();
String expectedHeading = "APPLICATION TEST DRIVE";
//Storing the text of the heading in a string
String heading = driver.findElement(By.xpath("//div[@class='text']//h2")).getText();
if(expectedHeading.equalsIgnoreCase(heading))
System.out.println("The expected heading is same as actual heading --- "+heading);
else
System.out.println("The expected heading doesn't match the actual heading --- "+heading);
}
@AfterTest
public void tearDown(){
driver.quit();
}
}
Using getText() Method To Get A Dropdown Text
There are multiple cases when we need to select a specific dropdown value, or maybe get the text values in a dropdown. We can use the getText() method to handle such scenarios as well. We will now see how we can get the values in a dropdown and print the same in the console.
package getText;
import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.Select;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
public class TextThruDropdown {
WebDriver driver;
@BeforeTest
public void setUp(){
System.setProperty("webdriver.chrome.driver","E:\\Softwares\\chromedriver.exe");
driver = new ChromeDriver();
}
@Test
public void headingText(){
driver.get("https://demoqa.com/select-menu");
driver.manage().window().maximize();
// Working on the third dropdown, viz, Old Style Select menu
WebElement drpdn = driver.findElement(By.id("oldSelectMenu"));
System.out.println("Clicking on the drop down");
Select se = new Select(drpdn);
List<WebElement> opt = se.getOptions();
System.out.println("The total number of options in the dropdown is : " +opt.size());
//Iterate through the list of options
System.out.println("The dropdown values are--- ");
for(WebElement options : opt){
System.out.println(options.getText());
}
}
@AfterTest
public void tearDown(){
driver.quit();
}
}
Using getText() Method To Get Alert Text
Just like the paragraph and dropdown, one can retrieve the text of an alert as well. Alerts are common with many applications, and hence there can be a need to check text present on it. The below code would fetch text from an alert.
package getText;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
public class AlertText {
WebDriver driver;
@BeforeTest
public void setUp(){
System.setProperty("webdriver.chrome.driver","E:\\Softwares\\chromedriver.exe");
driver = new ChromeDriver();
}
@Test
public void alertText(){
driver.get("https://demoqa.com/alerts");
driver.manage().window().maximize();
driver.findElement(By.id("confirmButton")).click();
String txt = driver.switchTo().alert().getText();
System.out.println("The text is - " +txt);
}
@AfterTest
public void tearDown(){
driver.quit();
}
}
How To Get Null Or Blank Text Values Using The getText() Method?
There might be cases when your web element doesn’t contain a text value at all. What do you think would happen if we apply the getText() method to such an element? As already told in the beginning, we would get an empty string in return. Let us see an example:
We have picked up the header image, which has no corresponding text, and we will be using the getText() method on the element. You will see an empty string is returned on the console without any error.
package getText;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
public class NullText {
WebDriver driver;
@BeforeTest
public void setUp(){
System.setProperty("webdriver.chrome.driver","E:\\Softwares\\chromedriver.exe");
driver = new ChromeDriver();
}
@Test
public void headingText(){
driver.get("https://demoqa.com");
driver.manage().window().maximize();
WebElement ele = driver.findElement(By.xpath("//header//img"));
System.out.println("The header text is - " +ele.getText());
}
@AfterTest
public void tearDown(){
driver.quit();
}
}
Difference Between getText() And getAttribute() In Selenium WebDriver
People generally get confused between getText and getAttribute methods a lot. Note that both these methods are entirely different and return different values.
getText() Method
The getText() method returns the visible inner text of a web element. You can refer to any of the above-stated examples to get the idea of getText() method.
getAttribute() Method
On the other hand, getAttribute() method fetches the value of the attribute we wish to retrieve. Let us see the getAttribute() method through a running example:

In the image above, we can see the multiple attributes in the input tag like id, type, class & placeholder. The getAttribute() method will retrieve the value corresponding to the attribute we pass as an argument. Let us see it through code-
package getText;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
public class GetAttribute {
WebDriver driver;
@BeforeTest
public void setUp(){
System.setProperty("webdriver.chrome.driver","E:\\Softwares\\chromedriver.exe");
driver = new ChromeDriver();
}
@Test
public void attributeText(){
driver.get("https://www.lambdatest.com/");
driver.manage().window().maximize();
// This line would store the attribute value as String variable
String attributeValue = driver.findElement(By.id("useremail")).getAttribute("placeholder");
System.out.println("The value is - " + attributeValue);
}
@AfterTest
public void tearDown(){
driver.quit();
}
}
getText() Method Vs. getAttribute() Method
Suppose we have a following HTML component:
<input name=”Title” type=”text” value=”GoogleTest” />https://www.googletest.com/
Now in this HTML component, if we use getText() method, we will get the value
“https://www.googletest.com/“
// Below code will give https://www.googletest.com/ as output
driver.findElement(By.name(“Title”)).getText();
And if we use getAttribute() method to get the value of attribute “value”, we will get “GoogleTest”.
// Below code will give GoogleTest as output
driver.findElement(By.name(“Title”)).getAttribute(“value”);
Though both of these values are a String value, both of these are entirely different.
Therefore, we mainly use getText() method to get the inner text of an HTML tag and getAttribute() method to get the attribute value of a Placeholder attribute.
We hope you are now clear with how to get text of an element in Selenium and the difference between getText() and the getAttribute() methods and understand which method to use where.
Espresso vs Appium: The Most Popular Automation Testing Framework
Appium vs Espresso
Mobile app automation testing has evolved as a crucial aspect of the mobile app development process to help deliver better quality solutions, under controlled time cycles and cost schedules. But for delivering bug-free app, choosing the best suitable automation testing framework for your app is very important. There are many automation testing frameworks available in the market with exceptional capacities. This blog is all about Appium vs Espresso and we will analyze which of these two most widely used Automation testing frameworks is preferable for your app testing.
Espresso was not preferred because of its flakiness and instability issues. But, from the time Google has brought Android Test Orchestrator, a Gradle test option, instability and unreliability of Android Espresso tests have vanished. This, in turn, is creating a serious problem for the most popular automation framework Appium.
Let’s find out in this blog if Espresso now comes with a power to kill Appium or Appium can hold its stand in this fiercely competitive market.

What is Appium?
It is an open source, cross-platform mobile app automation testing framework. Appium allows native, hybrid and web app testing and supports automation test on physical devices as well as emulators or simulators. The Appium server uses selenium web driver which permits platform independence and allows the user to use the same code for Android or iOS.
Advantages of using Appium
- Facilitates test execution without server machines
Appium is developed using cross-platform runtime environment like NodeJs which enables programmers to write server-side code in javascript. It is designed as an HTTP server and you can run the test without requiring a server machine.
- Does not require app code recompilation
Most of the automation testing tools require testers to alter app code. Some of the test automation frameworks require testing professionals to recompile the code according to the targeted mobile platforms. Appium enables testers to evaluate both cross-platform and native apps without recompiling and altering the code that often.
- Automates various types of mobile apps
Testers can avoid using different automation tools for different types of apps as Appium can be used for web apps, hybrid, and native apps too. It facilitates the testing of hybrid and mobile web apps as a cross-platform test automation framework. At the same time, it enables testers to test native apps through web driver protocol.
- Testers can use real devices, emulators, and simulators
Testers use real devices to evaluate mobile app’s usability and user experience more precisely. Although, to speed up the mobile app testing one needs to use emulators or simulators too. Appium helps testers to produce reliable test results and reduce testing time by supporting real devices, emulators and simulators.
- Provides a record and playback tool
In Appium, testers can use the inspector to accelerate testing through record and playback functionality. Appium inspector can record the behavior of native apps by inspecting their document object model (DOM). Record and playback tool can produce test scripts in a number of programming languages.
- Testers can automate apps without adding extra components
Testers can execute the same test across multiple mobile platforms without putting extra time and efforts or adding extra component. Appium simplifies automation by keeping complexities in Appium server.
- Supports several web driver compatible languages
You can integrate Appium with many testing frameworks and WebDriver – compatible languages including PHP, Java, Ruby, Javascript, C# and Objective C. Hence, a tester has the option to write test scripts in his preferred programming language.
Disadvantages of using Appium
- Common gestures
Appium lacks commonly used gestures like double-clicking in java-client libraries. It also does not support Android alert handling directly and the users cannot evaluate alert handling through native API. Testers have to put extra time and effort to test these gestures.
- No script execution on multiple iOS simulators
Simulators make it easier for testers to mimic internal behavior of the underlying iOS devices. Although Appium does not allow users to run multiple test scripts on multiple simulators simultaneously.
- Lacks the capability to recognize images
Appium cannot locate and recognize images automatically to evaluate games and apps precisely. The testers have to take help of screen coordinates to make Appium locate and recognize images.
- Does not support older versions of android
Appium supports only Android 4.2 and later and does not supports older APIs for Android. There are still many people using devices which run on older versions of Android and developers find it difficult to test mobile apps developed targeting older Android API level.
What is Espresso?
Espresso is a tool developed by Google which is used for testing the UI of Android apps. It automatically synchronizes your test actions with the user interface of the mobile app and ensures that the activity is started before the tests run.
Although when you execute an Espresso test you will have shared state in separate tests and some flakiness. For this Google came up with a solution. Android Test Orchestrator is a Gradle test option that helps in testing and increases the reliability of our automated test suites.
If you use Gradle build tools in any version of Android Studio below 3.0 then you also have to update the dependency setup. Let’s take a look at the advantages of using Android Espresso.
Advantages of using Espresso
- Integration with Gradle
The new Android Espresso now has the power of the Android Studio and Gradle that comes along with it. So now invoking your tests, running it or modifying it is just a matter of calling a Gradle command. This gives the full power of command line to the developer and makes testability much easier.
- Test Orchestrator
The new Android Espresso comes with the power of Android Test Orchestrator that allows you to run each of your app’s tests within its own invocation of Instrumentor. It ensures that there is minimum shared state and crashes being isolated. It allows you to filter the tests that you want to run and also distribute tests across devices. This implies that you have finer control over how your tests run.
- Less flakiness
The scalability of the test cycle in Android Espresso is high due to the synchronized method of execution. A built-in mechanism in Espresso that validates that the object is actually displayed on the screen. This saves test execution from breaking when confronted with “Objects not detected” and other errors.
- It’s easy to develop Espresso test automation
Test automation is based on Java and JUnit which Android developers are familiar with. There is no setup or ramping up to implement quality in the in-cycle stage of the app SDLC.
- Reliable and fast feedback
Android Espresso does not need any server to communicate with, instead, it runs side by side with the app and delivers fast results. It gives fast feedback to the code changes so that developers can move to the next bug fix.
- Simple workflow
Espresso allows developers to build a test suite as a stand-alone APK that can be installed on the target mobile alongside the app under test and be executed quickly.
Disadvantages of using Espresso
- It requires access to the application source code
Without the source code, you won’t be able to do anything. Also, There is a risk to get used to the in-built test synchronization and UI – then it might be hard to work with WebDriver.
- Narrow focus
If UI tests are required for both Android and iOS, it will be necessary to write twice, for two different systems. If tests require to work with Android outside the application (for example, open a received notification with a text message), you’ll have to use additional tools, such as UIAutomator.
- Knowledge of launching Android app on emulators required
It is desirable to have at least minimal experience of building and launching Android applications on emulators.
Conclusion
Appium and Espresso both can be used to perform UI testing on Android app but if you have to choose one of them then you need to decide on the bases of your requirements. What kind of app is it and what kind of testing you want to perform. Developers who want to perform UI testing for their native Android app should go for Android Espresso. Although, if the test needs to support iOS and Android both and you want to test at a functional level then you can use Appium.
Boundary Value Analysis Test Case Design Technique
Boundary Value Analysis Test case design technique is one of the testing techniques. You could find other testing techniques such as Equivalence Partitioning, Decision Table and State Transition Techniques by clicking on appropriate links.
Boundary value analysis (BVA) is based on testing the boundary values of valid and invalid partitions. The Behavior at the edge of each equivalence partition is more likely to be incorrect than the behavior within the partition, so boundaries are an area where testing is likely to yield defects.
Every partition has its maximum and minimum values and these maximum and minimum values are the boundary values of a partition.
A boundary value for a valid partition is a valid boundary value. Similarly a boundary value for an invalid partition is an invalid boundary value.
Tests can be designed to cover both valid and invalid boundary values. When designing test cases, a test for each boundary value is chosen.
For each boundary, we test +/-1 in the least significant digit of either side of the boundary.
Boundary value analysis can be applied at all test levels.
Example on Boundary Value Analysis Test Case Design Technique:
Example 1:
Assume, we have to test a field which accepts Age 18 – 56

Minimum boundary value is 18
Maximum boundary value is 56
Valid Inputs: 18,19,55,56
Invalid Inputs: 17 and 57
Test case 1: Enter the value 17 (18-1) = Invalid
Test case 2: Enter the value 18 = Valid
Test case 3: Enter the value 19 (18+1) = Valid
Test case 4: Enter the value 55 (56-1) = Valid
Test case 5: Enter the value 56 = Valid
Test case 6: Enter the value 57 (56+1) =Invalid
Example 2:
Assume we have to test a text field (Name) which accepts the length between 6-12 characters.

Minimum boundary value is 6
Maximum boundary value is 12
Valid text length is 6, 7, 11, 12
Invalid text length is 5, 13
Test case 1: Text length of 5 (min-1) = Invalid
Test case 2: Text length of exactly 6 (min) = Valid
Test case 3: Text length of 7 (min+1) = Valid
Test case 4: Text length of 11 (max-1) = Valid
Test case 5: Text length of exactly 12 (max) = Valid
Test case 6: Text length of 13 (max+1) = Invalid