by admin | Sep 20, 2021 | Automation Testing, Blog, Latest Post |
Programming language for automation testing should never be chosen based on popularity. There are several factors involved in choosing the best programming language for Test Automation. While some may prefer an option based on the testing team’s skillset, some teams go with the programming language which was used to develop the application to be tested. Are these factors alone enough? Let’s find out as we will be exploring the factors to consider when choosing a suitable programming language for your automation testing needs.
What programming language is best fit for test automation?
We, as an automation testing company have faced this question multiple times from our clients during sales calls. But imagine if this same question is asked to a Ruby programmer, the obvious answer will be “Ruby is an excellent programming language that will satisfy all your needs”. We shouldn’t be surprised with this answer as a person who has been using Ruby for a while will seem to feel that way.
But if we take a closer look at the reality, we will understand that the Ruby, Cucumber, and Selenium combination was very popular among automation testers a decade ago. The lack of community support and the emergence of other programming languages have resulted in Ruby losing its popularity. However, if you use Ruby even now, you will still love it as the aspects that made Ruby great are still there. It’s just the popularity that has gone down and that in no way makes a great language bad.
So let’s jump straight to the point and take a look at the factors an automation testing team should consider when choosing a programming language for automation testing:
1. The Automation Tools in use
If the automation testing tool you’re using is the right fit but supports only a few programming languages, then you would be left without much choice as you would have to choose from the languages that are supported.
The Common factor
There could be times where the programming language used during the development stages would also be supported by the test automation tool and have a lot of test automation libraries that you can benefit from. During such scenarios, it is better to opt for that language.
Your Forte
Everybody has their own strengths and weaknesses. So if your company or team has testers with a specific programming background and the tool also has good community support for the language, you can go for that programming language.
Checklist
Performing this action will definitely help you make a more educated choice that fits your needs. You would have to prepare a checklist of the valid reasons that would justify the choice of not just the programming language, but also of the automation tool. So what you would have in your hand is a report of whether you are taking a biased decision or a decision with the mindset that is unwilling to go out of your comfort zone. Be very point-blank with your justifications to get the best results.
Now let’s go over some of the popular programming languages we have used thus far in our automation testing projects. But more importantly, we will also be seeing what made the language popular and thereby help you have a better insight when making that choice for yourself.
Java
When Selenium and Appium emerged in the market, the trend to write automation tests using Java also picked up. If you go to any Selenium tutorial page right now, you will be able to see the examples using Java in the very beginning. Maven and Gradle are two major value-adds for Java as you will be able to develop some unique test automation framework features using Java if you know the design patterns and Maven capabilities. It is also fairly easy to find an automation tester who is knowledgeable in the Java, Selenium, and Appium combinations.
Python
According to a report, in the year 2020, 29% of the tutorials that were searched for was for Python and 19% for Java. Python’s popularity explosion was very quick and the stats are a clear reflection of that fact. But that alone doesn’t make Python one of the best programming languages for test automation. We, as a software testing company, have started recommending Python to our clients as the support for the tool, the libraries for automation testing, and the talents from different regions also grew in tandem with its popularity.
JavaScript
JavaScript was originally introduced for client-side programming. No one even thought about using it for server-side until Node.js came into the picture. To adopt a programming language for automation testing, it should have a package manager which allows to upload & download the test automation libraries and the automation tool should have 100% support for the language. When Node.js and npm were launched, JavaScript got the package manager it needed and gained more popularity.
C#
When Microsoft made the .net framework open-source with (.net core), it enabled the support for Linux & macOS. This is turn resulted in C#’s popularity shooting up drastically. Nowadays C# is used widely for the development of Desktop and Gaming Apps. If your team is using C# for application development, then C# is a great choice for automation testing as well.
Conclusion
You can’t choose a programming language for automation testing by others’ opinion, and that goes for our opinion as well. As a leading QA Automation Services provider, we use Python, C#, Java for multiple automation testing projects. But we do not pick them at random as every project has its own requirement. We never fail to perform the evaluation using the checklist to pick both the automation tool and the programming language.
by admin | Aug 31, 2021 | Automation Testing, Blog, Latest Post |
We live in an era where instant gratification is wanted by each customer that buys a product or service. Knowing this, it is critical that businesses keep up with their customers’ expectations to prevent losing sales and customers to their business rivals. To do this, it is a must that companies administer continuous testing to their software to ensure that it will always be up to par with what customers expect.
However, getting started with continuous testing may not be something that comes naturally to everyone. Plus, even if you do get the ball rolling for your continuous testing for your software development processes, you may still face some significant roadblocks. This blog post will shed some light on what these possible challenges may be and how to deal with them.
Major Roadblocks of Continous Testing: What You Can Do
There are three major roadblocks that may hinder the development of the software that you offer. Overcoming these challenges is the key to ensuring that you keep your clients happy and satisfied in the long run.
1 – Bottlenecks Caused by Time and Resource Restrictions
Many times, the time and resources that are needed to complete sustainable development and test automation are underestimated. This being said, testing may come to a halt if these considerations are not thought of beforehand.
What You Can Do: Before getting started, it is vital to establish a testing agenda that supports reuse and data-driven testing. It is also critical that the testing team keeps individual test results up to date with the improvements being made to the software.
2 – Complexity
It can be extremely easy to get lost in the technical complexity of the test automation process. This task requires a vast amount of technical knowledge to be done correctly.
What You Can Do: To ensure that a team overcomes this roadblock, they need to understand how to automate the testing process and connect the data and results across different technologies. An in-depth understanding of these processes will help weed out critical defects and reduce complexities while undergoing continuous testing.
3 – Addressing the Results
As the continuous testing process goes on, there will come a time where there an immense amount of results will be produced. These results will render an extremely high amount of false positives. Looking into and analyzing these results may become tedious and highly time-consuming for the testing team.
What You Can Do: It may take time, but it pays to look into each result and take each one into consideration when making a release decision about your application or software products.
Conclusion
It is often said that we are now living in the Digital Age. It is getting more and more difficult to please clients. Producing state-of-the-art software that is cutting edge enough to satisfy your clients can only be done when you continually improve your products. Following the tips mentioned above and working with a reliable, tried-and-tested QA team will help you make sure that your software is always at the top of its game.
Should you need help in test automation services for your software, Codoid is an industry leader in QA. We don’t say this just to brag; we say this because it is our passion to help guide and lead the Quality Assurance community. Our brilliant team of engineers love to attend and speak at: software testing meetup groups, forums, Software Quality Assurance events, and Automation testing conferences. Contact us and let us help you test your applications today!
by admin | Aug 30, 2021 | Automation Testing, Blog, Latest Post |
Nowadays Appium is being prominently used for identifying Xpath or locators in both Android and iOS apps. But when it comes to iOS, we would have to spend a lot of time configuring the real device using Xcode for Building the WebDriverAgent. If in case we don’t have the latest iOS Version, iPhone Version, and Xcode version, then we will be facing configuration issues. This is where a cloud testing platform like BrowserStack comes into the picture as an easy solution and alternative to such problems. So if you were looking for the steps to use BrowserStack for inspecting a mobile app’s locators, then you’re in the right place. As a leading automation testing company, we have been using BrowserStack for a long time. So in this BrowserStack Tutorial, we will be showing you how to use BrowserStack as a UI inspector for both Android and iOS apps.
App Live
Make sure to install BrowserStack and keep it ready to do the following steps. If you are still on the fence about purchasing an account, you can still use their trial period to check out how well it fits your needs. So now let’s see how we can test our mobile apps and view the developer logs via DevTools and also identify the mobile elements using the ‘Inspect’ option in DevTools.
Navigate to App Live as shown in the image below,
Let’s a look at the 7 easy steps that have to be followed in this BrowserStack Tutorial now,
i. Click on ‘Test with a Sample app’ as shown in the image.
ii. Upload your App using the given option. If it’s an Android app then upload your apk file here, if it’s iOS, upload your ipa file here.
iii. Select the device you want to test the app in.
iv. Once you have chosen the Real Device that you want to test in, the App will launch.
v. You will get a split-screen view as you see in the below image.
vi. So you will see a pane on the right side that will show the following three options, LOGCAT, INSPECT (BETA), and NETWORK
vii. Now, click on the ‘Inspect’ option, and then click on the ‘Play Button’ that appears to enable Inspector mode.
Inspector Mode
Once we have turned the Inspector mode on by clicking on the Play icon, we will easily be able to identify the locators and objects. All you have to do is hover the mouse over the element that you want to inspect on the mobile screen, and click on it. Once you have clicked, the XML snippet and the element we have selected will be highlighted as shown in the image below.
Right below the code snippers, we will be able to see the ‘Properties Table’ as well.
Highlighted XML code snippet:
>android.widget.ViewGroup
>android.widget.TextView
Properties Table:
The table will show attributes, keys, and values like the Resource-Id, Class name, Package Name, Index, Visible Text, etc…
Example:
Text: Login
Resource-Id: org.package:id/headerLabel
Class: android.widget.TextView
Package: org.package.alpha
Conclusion:
So using BrowserStack as a UI inspector is a very easy process that every tester must know. BrowserStack’s UI inspector has come in handy whenever there was any locator or object issue in the automation suite. We were able to come up with quick fixes and provide the best automation testing services to our clients as we were able to easily identify the locators and objects using BrowserStack. That is why specifically chose to cover that in this BrowserStack Tutorial. If you are looking to learn more about BrowserStack, kindly read our End-to-End guide on it.
by admin | Aug 23, 2021 | Automation Testing, Blog, Latest Post |
Dependency Injection is a design pattern used to create dependent objects outside a class and provide those objects to a class through different ways by implementing Inversion of Control. Using Dependency Injection, we can move the creation and binding of the dependent objects outside of the class that depends on them. JVM-Cucumber supports many different dependency injection frameworks, and one of them is Guice. As a leading QA company, we are always on the watch for new tools and frameworks to improve our testing process and so we tested out Guice as well. So in this blog, we will be showing you how to perform Cucumber Dependency Injection Using Guice.
Cucumber Dependency Injection Using Guice:
If you’re going to work in an automation framework from scratch or use an existing one, there are few aspects that you should keep in your mind. For example, you have to ensure that the framework is maintainable, easy to understand, helpful in avoiding coding duplicates, and quick to adapt to any changes. Though these are very basic aspects of a framework, it does require you to follow a few design principles and techniques in it. First off, let’s see why sharing the state between steps in Cucumber-JVM is a necessity.
Well, a Gherkin scenario is created by steps and each step depends on previous steps. That is why we must be able to share the state between steps. Since the tests are implemented as regular Java methods in regular Java classes. If steps are global, then every step in the same package or subpackage relative to the runner will be found and executed. This allows us to define one step in one class and another step in another class.
If you’re writing your first test, then there are high chances that you have just a few steps that can easily be fit into one class. But the real problem arises when there are a bunch of scenarios as it gets exponentially harder to maintain. So that is why dividing the steps between many classes is a good idea.
How do you share the state between different classes for Cucumber-JVM?
The recommended solution in Java is to use dependency injection. That is, inject a common object in each class with steps, an object that is recreated every time a new scenario is executed.
Note – Object State sharing is only for steps and not for scenarios.
Let’s take a look at an example scenario and find out how to share the state between multiple step definition files with a common object.
Example Scenario:
* David Orders a mobile phone from Amazon.
* He receives a defective product.
* He returns the product and requests a replacement.
* Amazon replaces the defective product.
Now, let’s split this example into the Gherkin format.
Cucumber-Guice\src\test\resources\Demo.feature
Feature: Replace the product
Scenario: Defective product should be replaced if user requests for replacement.
Given David orders the mobile phone from Amazon
When He returns the product for replacement
Then He will get a new product from Amazon
The example scenario we have seen talks about two different actions,
1. Purchasing a product from Amazon.
2. Returning a product.
So when we divide the implementation of the steps into different classes, the only file that gets affected is the steps definition. This is where Dependency Injection comes into play as we can use it to overcome this obstacle. So let’s see how to get it done using Guice.
The first change here would be to add new dependencies in the Maven POM File.
This is the dependency for Cucumber to use Guice:
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-guice</artifactId>
<version>1.2.5</version>
<scope>test</scope>
</dependency>
This dependency to use Google Guice:
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>4.1.0</version>
<scope>test</scope>
</dependency>
Maven POM File:
This is how the Maven POM file will look like:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Cucumber-Guice</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<junit.version>4.12</junit.version>
<cucumber.version>1.2.5</cucumber.version>
<selenium.version>3.7.1</selenium.version>
<maven.compiler.source>1.6</maven.compiler.source>
<maven.compiler.target>1.6</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>22.0</version>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-guice</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>4.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-java</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-core</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-junit</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-chrome-driver</artifactId>
<version>${selenium.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
The next step would be to create two classes for the steps. Let’s call them CustomerSteps and ProductSteps.
The idea here is that these classes will share state between steps that depend on the result of an earlier step in the scenario. It is known that sharing state can be done in different ways, and we have used a new class that holds the common data here.
Example:
src\test\java\DemoGuice\Steps\DemoCotainer.java
package DemoGuice.Steps;
import DemoGuice.Pages.ProductPage;
import DemoGuice.Pages.CustomerPage;
import cucumber.runtime.java.guice.ScenarioScoped;
@ScenarioScoped
public class DemoCotainer {
CustomerPage customerPage ;
ProductPage productPage;
}
In the above code, the democontainer class is annotated with @ScenarioScoped. So Guice will be able to acknowledge it as something that should be created and made available in different classes.
If we want to use this common data in each step definition file, we can add a constructor that takes the democontainer as an argument. This is where the injection occurs and so let’s take a look at an example to understand it better.
Example:
src\test\java\DemoGuice\Steps\ProductSteps.java
public class ProductSteps {
private DemoCotainer demoCotainer;
@Inject
public ProductSteps(DemoCotainer demoCotainer) {
this.demoCotainer = demoCotainer;
}
Now we can use the democontainer to access all of the common fields that are needed across the step definition classes. Here, we have annotated the field democontainer with @Inject. It is worth mentioning that you have the choice to annotate a constructor or a field to allow Guice to set the value. This enables the shared democontainer object to be available for all the steps definition classes.
Implementation of ProductSteps class:
src\test\java\DemoGuice\Steps\ProductSteps.java
package DemoGuice.Steps;
import com.google.inject.Inject;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
public class ProductSteps {
private DemoCotainer demoCotainer;
@Inject
public ProductSteps(DemoCotainer demoCotainer) {
this.demoCotainer = demoCotainer;
}
@Given("^David orders the mobile phone from Amazon$")
public void davidOrdersTheMobilePhoneFromAmazon() {
demoCotainer.productPage.orderMobilePhone();
}
@When("^He returns the product for replacement$")
public void heReturnsTheProductForReplacement() {
demoCotainer.productPage.requestForReturn();
}
}
Implementation of CustomerSteps class:
src\test\java\DemoGuice\Steps\CustomerSteps.java
package DemoGuice.Steps;
import com.google.inject.Inject;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
public class CustomerSteps {
@Inject
private DemoCotainer demoCotainer;
@Inject
public CustomerSteps(DemoCotainer demoCotainer) {
this.demoCotainer = demoCotainer;
}
@Then("^He will get new product from Amazon$")
public void heWillGetNewProductFromAmazon() {
demoCotainer.customerPage.receiveNewProduct();
}
}
Conclusion:
We hope you had an enjoyable read while also learning how to use Google Guice for performing cucumber dependency injection. Using Dependency Injection to organize our code better and share state between step definitions has been helpful in streamlining our process to provide the best automation testing services to all our clients. Make sure to stay connected with our blog for more informative blogs.
by admin | Aug 11, 2021 | Automation Testing, Blog, Latest Post |
Nowadays, data transfer from a client to a server or vice versa has become more concerning and significant. From the very beginning, using XML (Extensible Markup Language) has been one of the best ways for transferring data. Be it a configuration file or a mapping document, XML has made life easier for us by making quick data interchange possible by giving a clear structure to the data and helping the dynamic configuration & loading of variables. Then came JSON (JavaScript Object Notation), a competitive alternative and even possible replacement to XML. As a leading Test Automation Company, we make sure to always use the best tools in our projects. So in this blog, we will be listing the top 5 JSON Libraries every tester must know about and back it up with the need. But let’s take a look at a few basics before heading to the list.
What is JSON?
JSON is a data format that is both easy to read and write for us humans and easy to understand for the machines. It is mainly used to transmit data from a server to a web or mobile application. JSON is a much simpler and lightweight alternative to XML as it requires less coding and is smaller in size. This makes JSON faster when it comes to processing and transmitting data. Although it is written in JavaScript, JSON is language-independent.
Why is JSON so popular?
What makes JSON so popular is that it is text-based and has easy to parse data formatting that requires no additional code for parsing. Thus it helps in delivering faster data interchange and excellent web service results. The JSON library is open source and what makes it even better is that it is supported in all browsers. If we take a look at the other advantages of JSON, it has very precise syntax, the creation & manipulation of JSON are easy, and it uses the map data structure instead of XML’s tree data structure. We have added a sample syntax of JSON below:
{
“Id”: “101”,
“name: “Elvis”,
“Age”: 26,
“isAlive”: true,
“department”: “Computer Science”,
}
JSON Syntax Rules:
The syntax rules are very similar to the syntax rules of JavaScript, and they are as follows,
1. It should start and end with curly brackets.
2. Both keys and values must be indicated as strings.
3. Data are separated by commas.
Example:
4. Square brackets hold the arrays.
1. Jackson JSON Library
Jackson Library is an open-source library that is used by the Java community mostly because of its clean and compact JSON results that creates a very simple reading structure. In this library, dependencies are not required as it is independent. Mapping creation is also not required as it provides the default mapping for most of the objects which can be serialized. Though the system holds a large object or graph, it consumes a lesser amount of space to process and fetches the result.
Three steps to process the JSON by Jackson API
1. Streaming API
It enables us to read and write JSON content as discrete events. The implication here is that the JSON Parser reads the data and the JSON Generator writes the data. It can very easily be added to the maven repository by adding its dependency to the pom.xml file
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.1</version>
</dependency>
2. Tree Model
It converts the JSON content into a tree node, and the ObjectMapper helps in building a tree of JsonNode nodes. The tree model approach can be considered equivalent to the DOM parser that is used for XML. It is the most flexible approach as well. So similar to the Streaming API, the tree model can also be added to the maven repository by adding its dependency to the pom.xml file
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
3. Data Binding
Data binding lets us convert JSON to and from Plain Old Java Object (POJO) with the use of annotations. Here, the ObjectMapper reads and writes both types of data bindings (Simple Data Binding and Full Data Binding). We can add it to the maven repository by simply adding its dependency to the pom.xml file
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.12.3</version>
</dependency>
2. GSON Library
GSON is also an open-source library that was developed by Google. This library is special among the other JSON Libraries as it is capable of converting a JSON String into a Java Object and a Java Object into an equivalent JSON representation without calling the Java annotations in your classes.
Features of GSON
1. Open Source library
2. Cross-platform
3. Mapping is not necessary
4. Quite fast and holds low memory space
5. No Dependencies
6. Clean and compact JSON results.
Also, in GSON, we have the same three steps to process the JSON, and they are
1. Streaming API
2. Tree model
3. Data Binding
Adding it to the maven repository also has the same procedure as we have to just add it to its dependency in the pom.xml file
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
3. JSON-simple Library
It is a simple JSON library that is used for encoding and decoding the JSON text. It uses Map and List internally for JSON processing. We can use this JSON-simple to parse JSON data as well as write JSON to a file.
Features of JSON-simple
1. Lightweight API, which works quite well with simple JSON requirements.
2. No dependencies
3. Easy to use by reusing Map and List
4. High in performance
5. Heap-based parser
If you want to use a lightweight JSON library that both reads & writes JSON and also supports streams, you probably should choose this JSON-simple library.
The same process of adding its dependency to the pom.xml life can be carried out to add it to the maven repository.
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
4. Flexjson
It is also another JSON library that is used to serialize and deserialize Java objects into and from JSON. What’s special about Flexjson is its control over serialization that allows both deep and shallow copies of objects.
Normally, to send an object-oriented model or graph, other libraries create a lot of boilerplate to translate it into a JSON object. Flexjson tries to resolve this issue by providing a higher-level API like DSL.
If you know for a fact that you will be using a small amount of data in your application that will only need a small amount of space to store and read the object into JSON format, you should consider using Flexjson.
As usual, we can add it to the maven repository by adding its dependency to the pom.xml file.
<dependency>
<groupId>net.sf.flexjson</groupId>
<artifactId>flexjson</artifactId>
<version>2.0</version>
</dependency>
5. JSON-lib
JSON-lib is a java library for transforming beans, maps, collections, java arrays, and XML to JSON and back again to beans and DynaBeans. Beans are classes that encapsulate many objects into a single object (the bean), and DynaBeans, a Java object that supports properties whose names, data types, and values can be dynamically modified.
If you are about to use a large amount of data to store or read to/from JSON, then you should consider using JSON-lib or Jackson.
You can add the below dependency file to the pom.xml file to add it to the maven repository.
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
</dependency>
Conclusion:
We hope you are now clear which of these 5 JSON libraries would be apt for your use based on the points that we have discussed. As providing the best automation testing services is always a priority for us, we always explore all the viable options to streamline our process and enhance efficiency. With these libraries, you can parse the JSON String and generate Java objects or create a JSON String from your Java Objects. If you are having web services or any applications that result in a JSON response, then these libraries are very important for you.
Ultimately, if you want to handle large data with a good response speed, you can go with Jackson. But if all you need is a simple response, GSON is better, and if you are looking for any third-party dependencies, then you can go with JSON-simple or Flexjson.
by Anika Chakraborty | Jul 26, 2021 | Automation Testing, Blog, Latest Post |
Reducing the complexity of any process is always the key to better performance, similarly parsing the XML data to obtain a readable format of that XML file that we humans can understand is also a very important process. A simple equivalent to this parsing process would be the process of language translation. Let’s take the example of two national leaders discussing an important meeting. They could either choose to use a common language like English or talk in the languages they are comfortable with and use translators to solve the purpose. Likewise, the XML will be in a format that is easily understood by a computer, but once the information has been parsed, we will be able to read data from XML and understand it with ease.
As one of the leading QA companies in the market, we use different parsers based on our needs and so let’s explore which parser would be the perfect match for your need by understanding how they work. But before we explore how we can read data from XML, let us get introduced to XML first as there might be a few readers who may not know much about XML.
An Introduction to the XML:
XML stands for Extensible mark-up Language, and it’s primarily used to describe and organize information in ways that are easily understandable by both humans and computers. It is a subset of the Standard Generalized Mark-up Language (SGML) that is used to create structured documents. In XML, all blocks are considered as an “Element”. The tags are not pre-defined, and they are called “Self-descriptive” tags as it enables us to create our own customized tags. It also supports node-to-node interaction to fill the readability gap between Humans and Machines.
XML is designed to store and transfer data between different operating systems without us having to face any data loss. XML is not dependant on any platform or language. One can say that XML is similar to HTML as it neither acts as the frontend nor as the backend. For example, we would have used HTML to create the backend code, and that code would be passed to the frontend where it is rendered as a webpage.
Prerequisite:
There are a few basic prerequisites that should be ready in order to read data from XML, and we have listed them below,
1. Install any IDE(Eclipse/Intellij )
2. Make sure if Java is installed
3. Create a Java project in IDE
4. Create an XML file by using .xml extension
XML file creation:
So the first three steps are pretty straightforward, and you may not need any help to get it done. So let’s directly jump to the fourth and final prerequisite step, where we have to create an XML file manually in our Java project.
Navigate to the File tab in your IDE
– Create a new file
– Save it as “filename.xml”
The XML file will display under your Java project. In the same way, we can create the XML file in our local machine by using the .xml file extension. Later, we can use this XML file path in our program for parsing the XML. Let’s see the technologies for parsing the XML.
XML Parse:
XML parsing is nothing but the process of converting the XML data into a human-readable format. The XML parsing can be done by making use of different XML Parsers. But what do these parsers do? Well, parsers make use of the XSL Transformation (XSLT) processor to transform the XML data to a readable format and paves the way for using XML in our programs. The most commonly used parsers are DOM, SAX, StAX, Xpath, and JDOM. So let’s take a look at each parses one-by-one..
Using DOM Parser to Read data from XML:
DOM stands for Document Object Model. DOM is a parser that is both easy to learn and use. It acts as an interface to access and modify the node in XML. DOM works by building the entire XML file into memory and moving it node by node in a sequential order to parse the XML. DOM can be used to identify both the content and structure of the document. But the setback that comes with DOM is that it is slow and consumes a large amount of memory because of the way it works. So DOM will be an optimal choice if you are looking to parse a smaller file and not a very large XML file as everything in DOM is a node in the XML file. Let’s see how to parse the below XML by using the DOM parser.
Here is the XML File that we need to parse:
<?xml version = "1.0"?>
<Mail>
<email Subject="Codoid Client Meeting Remainder">
<from>Priya</from>
<empid>COD11</empid>
<Designation>Software Tester</Designation>
<to>Karthick</to>
<body>We have meeting at tomorrow 8 AM. Please be available
</body>
</email>
<email Subject="Reg:Codoid Client Meeting Remainder ">
<from>Kartick</from>
<empid>COD123</empid>
<Designation>Juniour Software Tester</Designation>
<to>Priya</to>
<body>Thanks for reminding me about the meeting. Will join on time</body>
</email>
</Mail>
DOM Parser:
package com.company;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
public class DOMParser {
public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException {
try {
File file = new File("E:\\Examp\\src\\com\\company\\xmldata.xml");
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();
Document doc = builder.parse(file);
doc.getDocumentElement().normalize();
System.out.println("Root element:: " + doc.getDocumentElement().getNodeName());
NodeList nList = doc.getElementsByTagName("email");
for (int temp = 0; temp < nList.getLength(); temp++) {
Node nNode = nList.item(temp);
System.out.println("\nCurrent Element :" + nNode.getNodeName());
if (nNode.getNodeType() == Node.ELEMENT_NODE) {
Element eElement = (Element) nNode;
System.out.println("Email Subject : "
+ eElement.getAttribute("Subject"));
System.out.println("From Name : "
+ eElement
.getElementsByTagName("from")
.item(0)
.getTextContent());
System.out.println("Designation : "
+ eElement
.getElementsByTagName("Designation")
.item(0)
.getTextContent());
System.out.println("Employee Id : "
+ eElement
.getElementsByTagName("empid")
.item(0)
.getTextContent());
System.out.println("To Name : "
+ eElement
.getElementsByTagName("to")
.item(0)
.getTextContent());
System.out.println("Email Body : "
+ eElement
.getElementsByTagName("body")
.item(0)
.getTextContent());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
We have created a DocumentBuilderFactory API to produce the object trees from XML, after which we’ve also created a document interface to access the XML document data. As stated earlier, the node is the base datatype for DOM here. From the code, we can see that the getDocumentElement() method will return the root of the element, and the getElementsByTagName() method will return the value of that particular tag.
Using the SAX Parser to Read data from XML:
The SAX parser is a simple event-based API that parses the XML document line-by-line using the Handler class. Everything in XML is considered to be “Tokens” in SAX. Unlike the DOM parser that we saw earlier, SAX does not load the entire XML file into memory. It also doesn’t create any object representation of the XML document. Instead, it triggers events when it encounters the opening tag, closing tag, and character data in an XML file. It reads the XML from top to bottom and identifies the tokens and call-back methods in the handler that are invoked. Due to the top to bottom approach, tokens are parsed in the same order as they appear in the document. Due to the change in the way SAX works, it is faster and uses less memory in comparison to the DOM parser.
SAX Parser:
try{
File file = new File("E:\\Examp\\src\\com\\company\\xmldata.xml");
SAXParserFactory saxParserFactory= SAXParserFactory.newInstance();
SAXParser saxParser= saxParserFactory.newSAXParser();
SaxHandler sax= new SaxHandler();
saxParser.parse(file,sax);
}
catch (Exception e){
e.printStackTrace();
}
}
}
In the above code, we have created an XML file and given its path in the code. The SAXParserFactory used in the code creates the new instance for that file. After that, we can create the object for the Handler class using which we parse the XML data. So we have called the handler class method by using the object. Now, let’s see how the Handler class and its method are created.
class SaxHandler extends DefaultHandler{
boolean from=false;
boolean to=false;
boolean Designation= false;
boolean empid= false;
boolean body=false;
StringBuilder data=null;
@Override
public void startElement(String uri, String localName,
String qName, Attributes attributes){
if(qName.equalsIgnoreCase("email")){
String Subject= attributes.getValue("Subject");
System.out.println("Subject:: "+Subject);
}
else if(qName.equalsIgnoreCase("from")){
from=true;
}
else if(qName.equalsIgnoreCase("Designation")){
Designation=true;
}
else if(qName.equalsIgnoreCase("empid")){
empid=true;
}
else if(qName.equalsIgnoreCase("to")){
to=true;
}
else if(qName.equalsIgnoreCase("body")) {
body = true;
}
data=new StringBuilder();
}
@Override
public void endElement(String uri, String localName, String qName){
if(qName.equalsIgnoreCase("email")){
System.out.println("End Element:: "+qName);
}
}
@Override
public void characters(char ch[], int start, int length){
// data.append(new String(ch,start,length));
if(from){
System.out.println("FromName:: "+new String(ch,start,length));
from=false;
}
else if(Designation){
System.out.println("Designation:: "+new String(ch,start,length));
Designation=false;
}
else if(empid){
System.out.println("empid:: "+new String(ch,start,length));
empid=false;
}
else if(to){
System.out.println("to:: "+new String(ch,start,length));
to=false;
}
else if(body){
System.out.println("body:: "+new String(ch,start,length));
body=false;
}
}
}
Our ultimate goal is to read data from XML using the SAX parser. So in the above example, we have created our own SAX Parser class and also extended the DefaultHandler class which has various parsing methods. The 3 most prevalent methods of the DefaultHandler class are:
1. startElement() – It receives the notification of the start of an element. It has 3 parameters which we have explained by providing the data that has to be used.
startElement(String uri, String localName,String qName, Attributes attributes)
uri – The Namespace URI, or the empty string if the element has no Namespace URI.
localName – The local name (without prefix) or the empty string if Namespace processing is not being performed.
qName – The qualified name (with prefix) or the empty string if qualified names are not available.
attributes – The attributes attached to the element. If there are no attributes, it shall be an empty attributes object.
The startElement() is used to identify the first element of the XML as it creates an object every time a start element is found in the XML file.
2. endElement() – So we have already seen about startElement(), and just as the name suggests, endElement() receives the notification of the end of an element.
endElement (String uri, String localName, String qName)
uri – The Namespace URI, or the empty string if the element has no Namespace URI
localName – The local name (without prefix) or the empty string if Namespace processing is not being performed.
qName – The qualified name (with prefix) or the empty string if qualified names are not available.
The endElement() is used to check the end element of the XML file.
3.characters() – Receives the notification of character data inside an element.
characters (char ch[], int start, int length)
ch – The characters.
start – The start position in the character array.
length – The number of characters that have to be used from the character array.
characters() is used to identify the character data inside an element. It divides the data into multiple character chunks. Whenever a character is found in an XML document, the char() will be executed. That’s why we append() the string to keep this data.
Using the JDOM Parser to Read data from XML:
So the JDOM parser is a combination of the DOM and SAX parsers that we have already seen. It’s an open-source Java-based library API. The JDOM parser can be as fast as the SAX, and it also doesn’t require much memory to parse the XML file. In JDOM, we even can switch the two parsers easily like DOM to SAX, or vice versa. So the main advantage is that it returns the tree structure of all elements in XML without impacting the memory of the application.
import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class JDOMParser {
public static void main(String[] args) throws JDOMException, IOException {
try{
File file = new File("E:\\Examp\\src\\com\\company\\xmldata.xml");
SAXBuilder saxBuilder = new SAXBuilder();
Document doc= saxBuilder.build(file);
System.out.println("Root element :" + doc.getRootElement().getName());
Element ele= doc.getRootElement();
List<Element> elementList = ele.getChildren("email");
for(Element emailelement: elementList){
System.out.println("Current element:: "+emailelement.getName());
Attribute attribute= emailelement.getAttribute("Subject");
System.out.println("Subject:: "+attribute.getValue());
System.out.println("From:: "+emailelement.getChild("from").getText());
System.out.println("Designation:: "+emailelement.getChild("Designation").getText());
System.out.println("Empid:: "+emailelement.getChild("empid").getText());
System.out.println("To:: "+emailelement.getChild("to").getText());
System.out.println("Body:: "+emailelement.getChild("body").getText());
}
}
catch (Exception e){
e.printStackTrace();
}
}
}
We have used the SAXBuilder class to transform the XML to a JDOM document. The getRootElement() is used to find the starting element of the XML and store all the elements from the XML to a list based on the starting element and iterate that element list. At the very end, we have used the getText() method to get the value of each attribute.
Using the StAX Parser to Read data from XML:
The StAX Parser is similar to the SAX Parser with just one difference. That major difference is that it employs 2 APIs (Cursor based API and Iterator-based API) to parse the XML. The StAX parser is also known as the PULL API, and it gets the name from the fact that we can use it to access the information from the XML whenever needed. The other standout aspect of the StAX parser is that it can read and also write the XML. Every element in the XML is considered as “Events”, and below is the code that we require for parsing the XML file using the StAX Parser.
XMLInputFactory factory = XMLInputFactory.newInstance();
XMLEventReader eventReader =
factory.createXMLEventReader(new FileReader("E:\\Examp\\src\\com\\company\\xmldata.xml "));
while(eventReader.hasNext()) {
XMLEvent event = eventReader.nextEvent();
switch(event.getEventType()) {
case XMLStreamConstants.START_ELEMENT:
StartElement startElement = event.asStartElement();
String qName = startElement.getName().getLocalPart();
if (qName.equalsIgnoreCase("email")) {
System.out.println("Start Element : email");
Iterator<Attribute> attributes = startElement.getAttributes();
String rollNo = attributes.next().getValue();
System.out.println("Subject " + Subject);
} else if (qName.equalsIgnoreCase("from")) {
EmailFrom = true;
} else if (qName.equalsIgnoreCase("empid")) {
Empid = true;
} else if (qName.equalsIgnoreCase("Designation")) {
Desination = true;
}
else if (qName.equalsIgnoreCase("to")) {
EmailTo = true;
}
else if (qName.equalsIgnoreCase("body")) {
EmailBody = true;
}
break;
case XMLStreamConstants.CHARACTERS:
Characters characters = event.asCharacters();
if(EmailFrom) {
System.out.println("From: " + characters.getData());
EmailFrom = false;
}
if(Empid) {
System.out.println("EmpId: " + characters.getData());
Empid = false;
}
if(Desination) {
System.out.println("Designation: " + characters.getData());
Desination = false;
}
if(EmailTo) {
System.out.println("to: " + characters.getData());
EmailTo = false;
}
if(EmailBody) {
System.out.println("EmailBody: " + characters.getData());
EmailBody = false;
}
break;
case XMLStreamConstants.END_ELEMENT:
EndElement endElement = event.asEndElement();
if(endElement.getName().getLocalPart().equalsIgnoreCase("email")) {
System.out.println("End Element : email");
System.out.println();
}
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}}
In StAX, we have used the XMLEventReader interface that provides the peek at the next event and also returns the configuration information.
The StartElement interface give access to the start elements in XML and the asStartElement() method returns the startElement event. It is important to note that the exception will be shown if the start element event doesn’t occur.
All character events are reported using the Characters interface. If you are wondering what would get reported as character events? The answer is that all the text and whitespaces events are reported as characters events.
The asCharacters() method returns the Characters from XML, and we will be able to get the data from XML as characters using the getData() method. Though it iterates each and every data from the XML and gives it in the form of a tree structure, it doesn’t return the start and end element events.
The EndElement class is used to point and return the end of the elements in an XML doc.
Using the Xpath Parser to Read data from XML:
The Xpath parser is a query language that is used to find the node from an XML file and parse the XML based on the query string. Now let’s take a look at an example code for better understanding.
File inputFile = new File("E:\\Examp\\src\\com\\company\\xmldata.xml");
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
// DocumentBuilder dBuilder;
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(inputFile);
doc.getDocumentElement().normalize();
XPath xPath = XPathFactory.newInstance().newXPath();
String expression = "/Mail/Email";
NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(doc, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); i++) {
Node nNode = nodeList.item(i);
System.out.println("\nCurrent Element :" + nNode.getNodeName());
if (nNode.getNodeType() == Node.ELEMENT_NODE) {
Element eElement = (Element) nNode;
System.out.println("From : " + eElement.getElementsByTagName("from").item(0).getTextContent());
System.out.println("EmpId : " + eElement.getElementsByTagName("empid").item(0).getTextContent());
System.out.println("Designation : " + eElement.getElementsByTagName("Designation").item(0).getTextContent());
System.out.println("TO : " + eElement.getElementsByTagName("to").item(0).getTextContent());
System.out.println("Body : " + eElement.getElementsByTagName("body").item(0).getTextContent());
}
In the above code, we used the XPath Factory for creating a new instance for the XPath. Then we have taken the XPath for the XML data and stored it as a String datatype. This String expression is called as “XPath Expression”.
Next, we have compiled the list of the XPath Expression by using the xPath.compile() method and iterated the list of nodes from the compiled expression using the evaluate() method.
We have used the getNodeName() method to get the starting element of the XML.
So once the XML document has been read, we would reuse the document and the XPath object in all the methods.
Conclusion
We hope you have found the parser that fits your requirement and in-process also enjoyed reading this article. So to sum things up, we have seen how each parser works to understand the pros and cons of each type. Choosing the apt parser might seem like a very small aspect when compared to the entire scale of the project. But as one of the best software testing service providers, we believe in attaining maximum efficiency in each process, be it small or big.