Automating Defect Logging with Jira and Selenium for algoQA Projects

Modified on Mon, 11 Mar 2024 at 04:03 PM

Integrating Jira with your Selenium framework can be helpful for managing issues that occur during automated testing. If a script fails generated by algoQA, Jira can be used to create a defect ticket automatically, which can then be assigned for resolution. This integration streamlines the defect management process and ensures that issues are addressed promptly.

Jira Overview

Jira is a tool primarily used for bug and issue tracking. It also supports project management features for all kinds of use cases, from requirements and test case management to Agile software development.

Jira is free to use for up to 20 users. However, if any organization requires additional features, such as support, storage, and a large user limit, must choose the commercial version. Since Jira is a bug/issue tracking tool, it enables the assignment, resolution, and implementation of bug life cycle management.

Setting up Jira Software with Google Account

Perform the following:

  1. Enter the URL Products | Atlassian into the browser bar. 
  2. Select Jira Software. In this page, select Get it free button on the right-top corner.
  3. Get started with Jira page appears. Click Google account button to create your account.
  4. Follow the on-screen prompts to create your first project.


Creating an API Token for Secure Integration with Jira

To enable secure connections between various software tools or automation scripts and your Jira account, you can create an API token. This token allows these tools to interact with Jira on your behalf without requiring your Jira password. This approach ensures security while facilitating smooth collaboration between different tools.

Perform the following to create an API token in Jira for use in Selenium Jira integration:

  1. Open a web browser and go to your organization's Jira website. For example, if your organization's Jira website is https://example.atlassian.net/, enter this URL in the address bar and press Enter.
  2. Log in to your Jira account using your credentials (username and password).
  3. After logging in, click icon next to your profile and settings icon or username on the top-right corner of the Jira dashboard.

  4. Select  the Account Settings option.
  5. Navigate to the Security tab and then, click Create and manage API tokens link.

  6. In the API Tokens screen, click the Create API token button.


  7. Enter the Label and click Create button.

  8. Upon creating the API token, click Copy button to copy the token to the clipboard and save it to appropriate location.


Integrating Selenium with Jira and logging defects in Jira using algoQA Project


Applies to JUnit, Selenium, Maven framework and Java as a programming language 


Prerequisites

  • Click here to download Eclipse IDE packages.
  • Maven Project must be configured.

To automatically create a Jira ticket when a Selenium test fails, and skip ticket creation if the same ticket has already been created in the project, perform the following:

  1. Add required dependencies in pom.xml
    1. In order to Integrate Jira with Selenium, use rcarz Jira-client. This is the wrapper built on top of the Jira Rest API Client. This file enables communication between Selenium and Jira, allowing Selenium tests to interact with Jira's features like creating and updating issues, and querying issue details. 
    2. Scripts generated by algoQA follow structured folder structure. Navigate to pom.xml in the project, and add the following dependency:

      <dependency>

            <groupId>net.rcarz</groupId>

            <artifactId>Jira-client</artifactId>

            <version>0.5</version>

            <scope>compile</scope>

       </dependency>

  2. Create basic tests using Selenium and Java
    In the com.demo.project package, two basic Selenium tests have been created: one designed to pass and one to fail.

    These tests can serve as examples for understanding Selenium test creation and execution.
    Selenium JIRA steps


  3. Create a utility to handle Jira issue
    Custom annotations can be used to mark specific test methods or classes to indicate that they should create Jira issues when certain conditions are met, such as a test failure. This allows you to automate the process of creating Jira issues for failed tests, making it easier to track and manage issues related to your test automation.
    To create a package inside the main folder of your project, for example com.demo.utility create a package to hold the utility classes related to Jira integration as shown.

     
    1. Create the JiraServiceProvider class: This class will handle the creation of Jira issues.
      Code snippet:
      package JiraUtility;


      import java.lang.annotation.RetentionPolicy;


      import java.lang.annotation.Retention;


      @Retention(RetentionPolicy.RUNTIME)
      public @interface JiraCreateIssue {
          boolean isCreateIssue();
      }




    2. Create the JiraCreateIssue annotation: This annotation will be used to mark specific test methods or classes that should create Jira issues.

      Code snippet:
      package JiraUtility;
      import net.rcarz.jiraclient.BasicCredentials;
      import net.rcarz.jiraclient.Field;
      import net.rcarz.jiraclient.Issue;
      import net.rcarz.jiraclient.Issue.FluentCreate;
      import net.rcarz.jiraclient.JiraClient;
      import net.rcarz.jiraclient.JiraException;


      public class JiraServiceProvider {
      private JiraClient Jira;
      private String project;
      private String JiraUrl;
      public JiraServiceProvider(String JiraUrl, String username, String password, String project) {
      this.JiraUrl=JiraUrl;
      // create basic authentication object
      BasicCredentials creds = new BasicCredentials(username, password);

      // initialize the Jira client with the url and the credentials
      Jira = new JiraClient(JiraUrl, creds);
      this.project = project;
      }
      public void createJiraIssue(String issueType, String summary, String description, String reporterName) {

      try {

      //Avoid Creating Duplicate Issue
      Issue.SearchResult sr = Jira.searchIssues("summary ~ \""+summary+"\"");

      if(sr.total!=0) {
      System.out.println("Same Issue Already Exists on Jira");
      return;

      }

      //Create issue if not exists
      FluentCreate fleuntCreate = Jira.createIssue(project, issueType);
      fleuntCreate.field(Field.SUMMARY, summary);

      fleuntCreate.field(Field.DESCRIPTION, description);
      Issue newIssue = fleuntCreate.execute();

      System.out.println("********************************************");

      System.out.println("New issue created in Jira with ID: " + newIssue);

      System.out.println("New issue URL is :"+JiraUrl+"/browse/"+newIssue);

      System.out.println("*******************************************");

      } catch (JiraException e) {


      e.printStackTrace();

      }

      }

      }


  4. Configure the Hooks File

    In a script generated folder structure, the 'hooks' file contains code that runs before or after scenarios in your test suite. If there is a failure in a scenario, you can use the 'hooks' file to perform certain actions, such as logging the failure, taking screenshots, or notifying team members.

    In the script folder structure, navigate to the Hooks file and update the code as shown in the following screenshot.

    Code snippet:

    package common;
    
    import static org.hamcrest.MatcherAssert.assertThat;
    import static org.junit.Assert.assertFalse;
    
    
    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.nio.file.Paths;
    import java.sql.Timestamp;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Base64;
    import java.util.Comparator;
    import java.util.Date;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    
    import org.apache.commons.io.FileUtils;
    import org.apache.log4j.Logger;
    import org.hamcrest.CoreMatchers;
    import org.junit.ClassRule;
    
    
    import org.openqa.selenium.OutputType;
    import org.openqa.selenium.TakesScreenshot;
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.chrome.ChromeDriver;
    import org.testng.asserts.SoftAssert;
    import com.aventstack.extentreports.ExtentReports;
    import com.aventstack.extentreports.ExtentTest;
    import com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter;
    //import com.cucumber.listener.Reporter;
    import com.google.common.io.Files;
    
    
    import JiraUtility.JiraServiceProvider;
    //import org.testng.asserts.SoftAssert;
    //vish commented following 3 lines
    //import cucumber.api.Scenario;
    //import cucumber.api.java.After;
    //import cucumber.api.java.Before;
    import common.CommonUtil;
    import common.TFSUtil;
    import common.WebBrowser;
    import io.cucumber.java.*;
    import ScreenRecorder.ScreenRecorderUtil;
    import org.monte.screenrecorder.ScreenRecorder;
    
    
    public class Hooks {
    public WebDriver driver;
    public static String userName;
    public static String password;
    private static List<Map<String, Object>> tstSteps = new ArrayList<>();
    final static Logger log = Logger.getLogger(Hooks.class);
    public static boolean closeBrowser=true;
    public static SoftAssert softAssertions=new SoftAssert();
    public static List<String> tagsExecuted=new ArrayList<String>();
    public static int passCount=0;
    public static int failCount=0;
    public static int skipCount=0;
    public static String JiraIntegration;
    private static String path = System.getProperty("user.dir");
    
    String EnableVideoCaptureForSuccess = CommonUtil.GetXMLData(
    Paths.get(path.toString(), "src", "test", "java", "ApplicationSettings.xml").toString(),
    "EnableVideoCaptureForSuccess");
    String EnableVideoCaptureForFailure = CommonUtil.GetXMLData(
    Paths.get(path.toString(), "src", "test", "java", "ApplicationSettings.xml").toString(),
    "EnableVideoCaptureForFailure");
    
    
    @Before
    
    public void init(Scenario scenario) {
    log.info("*********************************Execution started*******************************");
    String node=System.getProperty("Node");
    if(node == null || node.isEmpty())
    {
    node="Node1";
    }
    String url=System.getProperty("Url");
    System.out.println("Url-----------"+url);
    if(url != null && !url.isEmpty())
    {
    CommonUtil.appUrl=url;
    }
    String apiurl=System.getProperty("apiUrl");
    System.out.println("apiurl-----------"+apiurl);
    if(apiurl != null && !apiurl.isEmpty())
    {
    RestAssuredUtil.apiCmdUrl=apiurl;
    }
    String browserName=System.getProperty("browserName");
    System.out.println("browserName-----------"+browserName);
    if(browserName != null && !browserName.isEmpty())
    {
    CommonUtil.browserName=browserName;
    }
    YMLUtil.loadYML("src/test/java/", node);
    YMLUtil.loadObjectRepoYML("src/test/java/ObjectRepository.yml");
    YMLUtil.PayloadYML("src/test/java/Payload.yml", node);
    Timestamp timestamp = new Timestamp(System.currentTimeMillis());
    // Reporter.addScenarioLog("Start Time : "+timestamp);
    ExtentCucumberAdapter.addTestStepLog(""+scenario.getSourceTagNames());
    ExtentCucumberAdapter.addTestStepLog("Start Time : "+timestamp);
    if(EnableVideoCaptureForSuccess.toUpperCase().equals("TRUE")||EnableVideoCaptureForFailure.toUpperCase().equals("TRUE"))
    {
    try {
    ScreenRecorderUtil.startRecord("TestCase");
    } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }
    
    
    JiraIntegration = CommonUtil.GetXMLData(
    Paths.get(path.toString(), "src", "test", "java", "ApplicationSettings.xml").toString(),
    "JiraIntegration");
    
    //extent = new ExtentReports(System.getProperty("user.dir") + "/test-output/ExtentScreenshot.html", true);
    }
    
    //Method to wait for 2 minutes before executing the test case
    //This method is used to handle LRS execution so that execution will happen without locking the user to Login
    @Before("@waitinminutes")
    public void beforeScenario()throws Throwable {
    log.info("*********************************Scenario started*******************************");
    
    Thread.sleep(120000);
    CommonUtil.setCopiedCount(0);
    }
    
    
    @BeforeStep
    public void storeScenario(Scenario scenario)
    {
    WebBrowserUtil.sce=scenario;
    }
    
    @AfterStep
    public void addScreenshot(Scenario scenario)
    {
    
    WebBrowserUtil.takeEachStepScrenshot(scenario);
    
    }
    @After(order = 1)
    public void afterScenario(Scenario scenario) {
    
    if(EnableVideoCaptureForSuccess.toUpperCase().equals("TRUE")||EnableVideoCaptureForFailure.toUpperCase().equals("TRUE"))
    {
    try {
    ScreenRecorderUtil.stopRecord();
    } catch (Exception e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
    }
    if(EnableVideoCaptureForSuccess.toUpperCase().equals("TRUE")&&EnableVideoCaptureForFailure.toUpperCase().equals("FALSE"))
    { if(scenario.isFailed())
    ScreenRecorderUtil.deleteRecordedFile();
    }
    else if(EnableVideoCaptureForSuccess.toUpperCase().equals("FALSE")&&EnableVideoCaptureForFailure.toUpperCase().equals("TRUE"))
    { if(!scenario.isFailed())
    ScreenRecorderUtil.deleteRecordedFile();
    }
    
    }
    
    if(JiraIntegration.toUpperCase().equals("TRUE"))
    if (scenario.isFailed())
    {
    //scenario.getName()=>name of the failing scenario; scenario.getLine=>the line(s) in the feature file of the Scenario; scenario.getStatus()=>scenario Status
    
    String JiraIntegrationParameters = CommonUtil.GetXMLData(
    Paths.get(path.toString(), "src", "test", "java", "ApplicationSettings.xml").toString(),
    "JiraIntegrationParameters");
    String[] JiraPara = JiraIntegrationParameters.split("[,]", 0);
    // String description = "Scenario " + scenario.getName() + " is failed at line(s) " + scenario.getLine() + " with status " + scenario.getStatus();
    // String Summary = "summary1" + CommonUtil.error.substring(0, Math.min(CommonUtil.error.length(), 255));
    String description = CommonUtil.error;
    String Summary ="Scenario2 " + scenario.getName() + " is failed";
    // To limit the size of the summary to 255 characters
    Summary = Summary.substring(0, Math.min(Summary.length(), 255));
    // To remove special characters
    Summary = Summary.replaceAll("[^a-zA-Z0-9 ]", "");
    JiraServiceProvider JiraServiceProvider = new JiraServiceProvider(JiraPara[0], JiraPara[1], JiraPara[2],JiraPara[3]);
    JiraServiceProvider.createJiraIssue(JiraPara[4],Summary,description,JiraPara[5]);
    
    }
    
    log.info("*********************************Scenario ended*******************************");
    Timestamp timestamp = new Timestamp(System.currentTimeMillis());
    ExtentCucumberAdapter.addTestStepLog("End Time: "+timestamp);
    CommonUtil.setCopiedCount(0);
    CommonUtil.setCopiedCountTextNull();
    softAssertions=new SoftAssert();
    boolean semiAuto=false;
    String scenarioStatusLowercase = new String();
    scenarioStatusLowercase = scenario.getStatus().toString().toLowerCase();
    
    if(scenarioStatusLowercase.equals("skipped")) {
    skipCount++;
    }
    
    if (scenario.isFailed()||(scenarioStatusLowercase.equals("passed"))) {
    
    if(scenarioStatusLowercase.equals("passed")) {
    passCount++;
    } else if(scenarioStatusLowercase.equals("failed")) {
    failCount++;
    AutoHealUtil.SaveConfigDeatils();
    }
    
    String screenshotName = "Image_"+new Date().getTime();
    boolean flag=true;
    for(String tag : scenario.getSourceTagNames())
    {
    tagsExecuted.add(tag);
    if(tag.toLowerCase().contains("api"))
    {
    flag=false;
    break;
    }
    }
    if(flag)
    {
    driver = WebBrowser.getBrowser();
    WebBrowserUtil.takeScrenshot(scenario);
    
    
    }
    }
    for(String tag : scenario.getSourceTagNames())
    {
    closeBrowser=true;
    if (tag.contains("usesamesession"))
    {
    closeBrowser=false;
    break;
    }
    }
    String testcaseId="";
    for(String tag : scenario.getSourceTagNames())
    {
    if (tag.contains("set2") || tag.contains("semiauto") || tag.contains("set3") || tag.contains("set21") || tag.contains("set22") || tag.contains("set23"))
    {
    if(closeBrowser)
    {
    WebBrowser.closeBrowserInstance();
    }
    }
    if (tag.contains("semiauto"))
    {
    semiAuto = true;
    }
    if (scenario.isFailed() && semiAuto)
    {
    semiAuto = false;
    throw new CustomException("Semi Auto test cases may fail due to OTP / Captcha.");
    }
    if(tag.contains("test"))
    {
    testcaseId=tag;
    }
    
    String SubmitTfsResult = CommonUtil.GetXMLData(Paths.get( System.getProperty("user.dir").toString(), "src", "test", "java", "ApplicationSettings.xml").toString(), "SubmitResultToTFS");
    boolean tfsResult= Boolean.parseBoolean(SubmitTfsResult);
    if(tfsResult) {
    int testPointId=0, testPlanId=0, testRunId=0,testCaseId=0;
    TFSUtil tfsUtil = new TFSUtil();
    testPointId = tfsUtil.getTestPointId(testCaseId)[0];
    testPlanId= tfsUtil.getTestPointId(testCaseId)[1];
    testRunId = tfsUtil.getTestRunId(testCaseId, testPointId, testPlanId);
    System.out.println(tstSteps);
    tfsUtil.updateResultsToTFS(testCaseId,testPointId, testRunId, tstSteps, userName, password);
    tstSteps.clear();
    
    }
    
    }
    softAssertions=new SoftAssert();
    }
    
    
        @After(order = 0)
    public void closeBrowser() {
    log.info("*********************************Execution ended*******************************");
    System.out.println("------------------------------");
    System.out.println(" Status - ");
    System.out.println("------------------------------");
    if (WebBrowser.isBrowserOpened() && closeBrowser)
    {
    WebBrowser.closeBrowserInstance();
    }
    String abc=System.getProperty("user.dir");
    File dir = new File(abc+"//output//");
    File[] files = dir.listFiles();
    File lastModified = Arrays.stream(files).filter(File::isDirectory).max(Comparator.comparing(File::lastModified)).orElse(null);
    System.out.println(lastModified);
    
    try {
    
    int totalCount = passCount+failCount+skipCount;
    String json = "{\"TotalTest\":"+String.valueOf(totalCount)+","+"\"passed\":"+passCount+","+"\"failed\":"+failCount+","+"\"skipped\":"+skipCount+"}";
    String path= lastModified+"//Execution_status.json";
    System.out.println("PATH :"+path);
    BufferedWriter writer = new BufferedWriter(new FileWriter(new File(path)));
    writer.write(json);
    writer.close();
    FileWriter myWriter = new FileWriter(lastModified+"//ExexutedTagDetails.txt");
    Set<String> uniqueTag = new HashSet<String>(tagsExecuted);
    String test="";
    String set="";
    String otherTags="";
    for (String tag : uniqueTag) {
    if(tag.contains("@test"))
    {
    test+= test=="" ? tag: ","+tag;
    
    }
    else if(tag.contains("@set"))
    {
    set+= set=="" ? tag: ","+tag;
    
    }
    else
    {
    otherTags+= otherTags=="" ? tag: ","+tag;
    
    }
    }
    
    myWriter.write("Test Tags:\n");
    myWriter.write(test+"\n\n");
    myWriter.write("Set Tags:\n");
    myWriter.write(set+"\n\n");
    myWriter.write("Other Tags:\n");
    myWriter.write(otherTags+"\n");
    myWriter.close();
    } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    
    }
    
    }
    
    


    Project folder structure is displayed similar to the example shown in the following screenshot:


  5. Get the Project Key
    Perform the following:
    1. Navigate to Projects Main Menu and select View All Projects as follows:
    2. Choose the respective project.
    3. In the Project page, Key will be displayed next to the corresponding project name.
       
  6. Configure Applications.XML file 
    1. Navigate to the Application Settings. XML file in your script generated folder
    2. Set the Jira Integration parameter to true and add all the required parameters as shown: 

      When you create a JiraServiceProvider object, consider the following parameters:

      Field Description
      JiraUrlIndicates company's Jira URL. If you are working on Jira cloud, then URL will be: https://<organisation>.atlassian.net
      UsernameUser is a parameter used for authentication, but its format can vary based on configuration. For example, format is <Username>@gmail.com
      Password When using Jira Cloud, it is necessary to pass the API Token as the password. Using your login password might not work. For example, the API token could be lorelimpusm12uijk.
      Project KeyThis refers to the project key, which is typically a short, two-letter identifier (e.g., "AS") assigned by Jira. Using the full project name, such as "Algoshack Project," might not work, so it's important to always use the short version.
      Issue Type Indicates the category of an issue. For example, Bug.
      Reporter NameName of the person in the project who is reporting the bug.


  7. Configure the testng.xml file

    After all the required set up, testng.xml file must be configured to run the tests. 
    The following code displays sample xml file. (You can use your existing testng.xml file to run your tests).

    Code snippet:
    ?xml version="1.0" encoding="UTF-8"?>
    
    <suite name="Default suite" parallel="methods" verbose="3">
    <listeners>
    <listener class-name="com.demo.listener.TestListener" />
    </listeners>
    <test name=" First test" skipfailedinvocationcounts="false">
    <classes>
    <class name="com.demo.project.HomePageTest">
    </class>
    </classes>
    </test>
    </suite>


  8. Execute your tests

    Upon completing the setup, execute the tests. After test execution, any failures must automatically trigger the creation of a Jira ticket. Results will be visible in the console section for further analysis and verification.

    The following screenshot represents sample console output:



    Successful completion of Selenium tests triggers automatic creation of a Jira Issue, with the console displaying the Issue ID and providing a URL to navigate to the specific issue.

  9. Verifying Jira ticket creation
    Navigate to the Jira dashboard to check whether an issue has been created or not. In the Jira dashboard, click on the project dashboard to see if a new issue has been created along with a particular test step failure details and Issue details as shown:




    Most of the operations performed through the Jira user interface can be run using the REST API client. The Jira REST API client allows testers to automate the issue tracking and logging process, reducing the need for manual effort and attention.

 


















Was this article helpful?

That’s Great!

Thank you for your feedback

Sorry! We couldn't be helpful

Thank you for your feedback

Let us know how can we improve this article!

Select atleast one of the reasons
CAPTCHA verification is required.

Feedback sent

We appreciate your effort and will try to fix the article