Thursday, August 11, 2016

JIRA Customization - Jenkins Integration for running Automated Tests


Goals 

  • Get all issues of given Filter
  • Get custom field from every Test Case
  • Maintain a Map of JIRA ID to Custom Field
  • Create a TestNG XML suite Programmatically and run it
  • Get results programmatically from TestNG
  • Update the issues by using Reverse mapping of Custom Field to JIRA ID

How to Achieve

  • Create a Filter
  • project = MFS AND issuetype = "Test Case" AND "Test Cycle" = "Version 4.0 - Cycle 1" AND "Automation Status" = Automated AND status  = "In Progress"
  • Build jar from the code at the end of this Post and run on Jenkins.
  • This code needs following inputs
    • JIRA URL
    • JIRA Credentials
    • Filter like “filter=10112”
    • customField="customfield_10032"
    • Note: You can get custom field name from JIRA Rest API and this name is different from what you see in JIRA UI

package com.company.qa.project.test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.testng.IReporter;
import org.testng.ISuite;
import org.testng.ISuiteResult;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.TestNG;
import org.testng.xml.XmlClass;
import org.testng.xml.XmlInclude;
import org.testng.xml.XmlSuite;
import org.testng.xml.XmlTest;

import net.rcarz.jiraclient.BasicCredentials;
import net.rcarz.jiraclient.Field;
import net.rcarz.jiraclient.Issue;
import net.rcarz.jiraclient.JiraClient;
import net.rcarz.jiraclient.JiraException;

public class JIRATest implements IReporter{

ArrayList<String> failedTests = new ArrayList<String>();
Collection<String> passedTests = new ArrayList<String>();
Collection<String> skippedTests = new ArrayList<String>();
BasicCredentials creds = new BasicCredentials("username@mail.com", "password");
JiraClient jira = new JiraClient("https://domain.atlassian.net", creds);
String filter = "filter=10112";
String customField = "customfield_10032";
   public static void main(String[] args) {
    JIRATest jiraTest = new JIRATest();
    jiraTest.runTests();
   
   }
   
   public void runTests(){
       
       TestNG testNG = new TestNG();
       XmlSuite suite = new XmlSuite();
       suite.setName( "MFS Automated Suite" );
       XmlTest xmlTest = new XmlTest(suite);
       xmlTest.setName( "MFS Autmation Test" );
       xmlTest.setVerbose( 0 );
       List<Class> ListenerClasses = new ArrayList<Class>();
       ListenerClasses.add(com.company.qa.project.test.JIRATest.class);
       testNG.setListenerClasses(ListenerClasses);
       try {
        HashMap<String,ArrayList<String>> map = new HashMap<String,ArrayList<String>>();
           /* Retrieve issue TEST-123 from JIRA. We'll get an exception if this fails. */
           Issue.SearchResult sr = jira.searchIssues(filter);
           System.out.println("Total number of Tests to be run: " + sr.total);
           for (Issue i : sr.issues){
               /* Pretend customfield_1234 is a text field. Get the raw field value... */
               Object cfvalue = i.getField(customField);
               
               /* ... Convert it to a string and then print the value. */
               String[] cfstrings = Field.getString(cfvalue).trim().split("#");
               if(cfstrings.length == 2){
                ArrayList<String> methods = map.get(cfstrings[0]);
                if(null == methods){
                methods = new ArrayList<String>();
                methods.add(cfstrings[1]);
                }else{
                methods.add(cfstrings[1]);
                }
                map.put(cfstrings[0], methods);
               }
           }
           
           for (Map.Entry<String,ArrayList<String>> entry : map.entrySet()) {
             String key = entry.getKey();
             ArrayList<String> methods = entry.getValue();
             XmlClass xmlClass = new XmlClass(key);
                 xmlTest.getClasses().add(xmlClass);
                 for(String method: methods){
                 XmlInclude xmlInclude = new XmlInclude(method);
                 xmlClass.getIncludedMethods().add(xmlInclude);
                 }
           }
           
          
           
           System.out.println("Running Tests using command ..");
           List<XmlTest> tests = suite.getTests();
           for(XmlTest test: tests){
            List<XmlClass> classes = test.getClasses();
            for(XmlClass xmlClass: classes){
            List<XmlInclude> methods = xmlClass.getIncludedMethods();
            for(XmlInclude xmlInclude : methods){
            System.out.println("Class: "+xmlClass.getName()+" Method: "+xmlInclude.getName() );
            }
            }
           }
           
           
           testNG.setXmlSuites( Arrays.asList( suite ) );
           testNG.setUseDefaultListeners( false );
           testNG.run();
           
       } catch (JiraException ex) {
           System.err.println(ex.getMessage());

           if (ex.getCause() != null)
               System.err.println(ex.getCause().getMessage());
       }
   }

@Override
public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {
//Iterating over each suite included in the test
try{
HashMap<String,String> mapOfTCAndMethods = new HashMap<String,String>();
Issue.SearchResult srlt = jira.searchIssues(filter);
       System.out.println("Total number of Tests to be run: " + srlt.total);
       for (Issue i : srlt.issues){
           /* Pretend customfield_1234 is a text field. Get the raw field value... */
           Object cfvalue = i.getField(customField);
           
           /* ... Convert it to a string and then print the value. */
           String[] cfstrings = Field.getString(cfvalue).trim().split("#");
           if(cfstrings.length == 2){
            mapOfTCAndMethods.put(Field.getString(cfvalue).trim(),i.getKey());
           }
       }
       for (ISuite suite : suites) {
           //Following code gets the suite name
           String suiteName = suite.getName();
   //Getting the results for the said suite
   Map<String,ISuiteResult> suiteResults = suite.getResults();
   for (ISuiteResult sr : suiteResults.values()) {
       ITestContext tc = sr.getTestContext();
       System.out.println("Passed tests for suite '" + suiteName +
            "' is:" + tc.getPassedTests().getAllResults().size());
       System.out.println("Passed Tests : ");
       for (ITestResult s :  tc.getPassedTests().getAllResults()) {
        System.out.println("Marking "+mapOfTCAndMethods.get(s.getTestClass().getName()+"#"+s.getMethod().getMethodName())+" as Passed ...");
            Issue issue = jira.getIssue(mapOfTCAndMethods.get(s.getTestClass().getName()+"#"+s.getMethod().getMethodName()));
            issue.transition().execute("Pass");
       }
       System.out.println("Failed Tests : ");
       for (ITestResult s :  tc.getFailedTests().getAllResults()) {
        System.out.println("Marking "+mapOfTCAndMethods.get(s.getTestClass().getName()+"#"+s.getMethod().getMethodName())+" as Failed ...");
            Issue issue = jira.getIssue(mapOfTCAndMethods.get(s.getTestClass().getName()+"#"+s.getMethod().getMethodName()));
            issue.transition().execute("Fail");
       }
       System.out.println("Failed tests for suite '" + suiteName +
            "' is:" +
            tc.getFailedTests().getAllResults().size());
       System.out.println("Skipped tests for suite '" + suiteName +
            "' is:" +
            tc.getSkippedTests().getAllResults().size());
     }
       }
}catch(Exception e){
e.printStackTrace();
}
}
}
~Yagna

Wednesday, August 10, 2016

Test Case Management using JIRA Customization - Version 2.0

In the last Post we have seen how we can do Test Management by creating a Custom field "Test Status at Build". But due to change in requirements I tried another approach.

 Now go to Issues page from JIRA Administration->Issues as shown in below picture



Then Add Issue Type like below


Now go to Issue Type Schema and associate it with some Issue Type Schema



Now go to Workflows and Import from Marketplace a new Workflow called "Test Case Management Workflow" 

Assign this workflow to Workflow Scheme at Workflow Schemes and Assign "Test Case" issue type to this Workflow Scheme



Add a new Screen 


Now Add a New Screen Scheme


Associate Issue Type with a Screen Scheme


Create following Custom Fields and associate with Screens


Project/Component/Story
  • In JIRA Project will be top level entity
  • Every Project will be having several Components
  • Stories will be created per Project and will be associated with one or more Components
New IssueType called Test Case added into JIRA with CFs 

Screen Shot 2016-08-10 at 6.49.32 PM.png

Requirement Traceability

Test Case should be linked to Requirement

Screen Shot 2016-08-10 at 6.48.18 PM.png







 This allows Requirement Traceability

Screen Shot 2016-08-10 at 6.47.01 PM.png

Test Cases for Different Platforms(OS)

Screen Shot 2016-08-10 at 3.55.43 PM.pngScreen Shot 2016-08-10 at 3.57.06 PM.png

Test Cases for Different Platforms(OS)

  • Create Test Case for a Platform and upon review Clone them if Test Steps are same for all the Platforms
  • Create Different Test Cases for different Platforms if they are platform dependent Test Cases
  • Link a Test Case of different Environments

Screen Shot 2016-08-10 at 3.58.21 PM.png

Test cycle creation

Screen Shot 2016-08-10 at 4.01.08 PM.png

Clone Regression Tests from Previous Test Cycle

  • Get All Test Cases from Test Case Repository which are not invalid
  • Clone them
  • Change the Test Cycle and Assign them

Assign Test Cases for Execution - Using JQL and Bulk Upload

Screen Shot 2016-08-10 at 4.03.30 PM.png


Test case assignment for automation

  • Change Automation Status as “To be Automated” in Test Case
  • Create a Sub-Task for Test Case
  • Add to appropriate Test Cycle
  • Assign it to a QA Engineer



Test cycle execution assignments
Screen Shot 2016-08-10 at 4.07.31 PM.png
Automation assignments
Screen Shot 2016-08-10 at 4.09.27 PM.png
User Dashboard
Screen Shot 2016-08-10 at 4.14.01 PM.png
Test Execution
Screen Shot 2016-08-10 at 4.15.58 PM.png
Automation Activity

  • QA Engineer looks at User Dashboard for Automation ActivitiesScreen Shot 2016-08-10 at 4.18.41 PM.png
  • QA Engineer will put it in Progress
  • Complete it and mark it as Done
  • Change Automation Status as “Automated” in Test Case
  • Update “Automation Code” with Fully Qualified Class Name and @Test Method Name

Test progress in a Test cycle
Screen Shot 2016-08-10 at 4.20.13 PM.png

Test progress at Component level
Screen Shot 2016-08-10 at 4.22.55 PM.png

Test progress at Feature Level
Screen Shot 2016-08-10 at 6.50.13 PM.png

Automation Progress
Screen Shot 2016-08-10 at 4.24.41 PM.png

Test execution report with teststeps
Screen Shot 2016-08-10 at 4.27.12 PM.png


No. of Test Cases added in Latest Release
Screen Shot 2016-08-10 at 4.28.56 PM.png

No. of Regressions across Versions

  • Bug should be linked to Test Case with “is regressed by” link type, if it a regression Bug.
  • If not it should be linked to Test Case with “is caused by”

Screen Shot 2016-08-10 at 4.38.55 PM.pngScreen Shot 2016-08-10 at 4.42.14 PM.png


No. of regressions within Release
Screen Shot 2016-08-10 at 4.44.06 PM.png


Test execution history for a Test Case


Add-ons Required



Issue Links Used
Screen Shot 2016-08-10 at 6.52.11 PM.png


Filters Used
 
Filter Name
JQL
Bugs with Test Cases
project = MFS AND linkType = "is regressed by" AND issuetype = Bug AND affectedVersion = "Version 3.0"
Test Case Repository
project = MFS AND issuetype = "Test Case" AND (linkType not in (clones) OR linkType is EMPTY)
Test Cases Added in Latest Version
project = MFS AND issuetype = "Test Case" AND affectedVersion = "Version 4.0"
Test Cases Caused Regression
project = MFS AND linkType = regresses AND issuetype = "Test Case"
Test Cases With Open Bugs
project = MFS AND issuetype = "Test Case" AND status = "TC - Failed" AND linkedIssueType = Bug AND linkedIssueStatus = "To Do"
Test Execution of Latest Test Cycle
project = MFS AND issuetype = "Test Case" AND "Test Cycle" = "Version 4.0 - Cycle 1"
Requirement Traceability
project = MFS AND issuetype = Story AND linkType = "is requirement of"
All Bugs with Test Cases
project = MFS AND (linkType = "is caused by" OR linkType = "is regressed by") AND issuetype = Bug AND affectedVersion = "Version 3.0"
Automation Progress
project = MFS AND issuetype = Sub-task AND "Test Cycle" = "Version 4.0 - Cycle 1"
Regression Bugs with Test Cases
project = MFS AND linkType = "is regressed by" AND issuetype = Bug AND affectedVersion = "Version 3.0"

~Yagna