Check for broken links on your website using Postman

If you are using Postman for your API Testing, then you can also you the same to automatically crawl all the pages on our website and check every link for a healthy HTTP status code.

This can be achieved using 2 simple API requests in Postman.

First lets create a new Collection and an Environment in Postman, where you can specify

  • root_url
  • start_url

Specify the values for the root_url and start_url.

root_url as https://linkeshkannavelu.com/

start_url as https://linkeshkannavelu.com/category/software-testing/selenium/

Create a simple request with Get method and enter url as {{start_url}} and in the Tests tab enter the following code.

// set environment variables to default values
postman.setEnvironmentVariable('links', '[]');
postman.setEnvironmentVariable('url', postman.getEnvironmentVariable('start_url'));
postman.setEnvironmentVariable('index', -1);

 

Initialize

Create a second request – Get method and enter URL as {{url}} and in the Tests tab enter the following code.


// Tests and custom scripts can be written in JavaScript.

// get environment variables
var start_url = postman.getEnvironmentVariable('start_url');
var root_url = postman.getEnvironmentVariable('root_url');
var links = JSON.parse(postman.getEnvironmentVariable('links'));
var url = postman.getEnvironmentVariable('url');
var index = parseInt(postman.getEnvironmentVariable('index'));

// increment index counter to access links in array to check
index = index + 1;

// test if link works
if (responseCode.code > 400) {
 console.log("This link is broken: ", url);
 tests["Link works"] = false;
} else {
 tests["Link works"] = true;
}

// if the current url includes the start_url, then this is an internal link and we should crawl it for more links
if (url.includes(start_url)) {

 // load the response body as HTML using cheerio, get the <a> tags
 var $ = cheerio.load(responseBody);

 $('a').each(function (index) {

 var link = $(this).attr('href');

 // add links to the links array if not already in there
 // if you have additional links you would like to exclude, for example, ads, you can add this criteria as well
 if (!links.includes(link)) {
 links.push(link);
 }
 });
}

// if we've gone through all the links, return early
if (links.length - 1 === index) {
 console.log('no more links to check');
 return;
}

// if link is a relative one, prepend with root_url
url = links[index]
if (! /^https?:\/\//.test(url)) {
 url = root_url + url;
}

// update environment variable values
postman.setEnvironmentVariable("links", JSON.stringify(links));
postman.setEnvironmentVariable("url", url);
postman.setEnvironmentVariable("index", index);

// continue calling the same request until all links are checked
postman.setNextRequest('Check URL');

Now Open “Runner” Select the Collection, Select the Environment and Click on Start Run Button.

Run

You can see Postman in action crawling all the links until there are no more links to check.

You can also simply download the Postman Collection and import it into your Postman.

SoapUI – WADL and Test Coverage

The most important part after developing your API is to provide a good documentation about this.

For Restful services, it is always good to provide a WADL document describing your API.

This provides a machine readable specification that can drive a human readable view as well as various testing tools. There are several other purposes of WADL.

  • DevOps can more quickly diagnose and correct problems when parts of the larger system can be tested in isolation.
  • When working in a large team base, developers who is gonna consume your exposed API’s should not approach the developers who did it for knowing more about your API functionality.
  • Developers/QA Engineers will be able to use the SoapUI project as an example of how to access the API.
  • For integrating our APIs with API Developer Portal or any other centralized ESB.
  • To derive the test coverage for our API tests.
  • You can do a rest code generation using WADL2Java in SoapUI.

Different documentations for your Web API:

There are different documentations available for your web API’s like

Even Ready API Pro version has the ability to import/create tests for rest services from the above documentations or framework with the help of external plugins.

So most of the services that I am working on are restful and I recommend WADL more as a standard. It’s always your developer’s responsibility to provide a proper documentation for your web API. But sometimes it may not happen. So in that case let’s see how you can generate WADL on your own.

How to Generate a WADL:

There are several ways that you can use to generate the WADL for your API’s and each has its own Pros and Cons.

WADL File Generator in .NET

As I am working only on .net projects, this solution will use the  leeksnet.AspNet.WebApi.Wadl package to generate the WADL. After installing this package along with its dependencies in your API project, we can be able to see the generated WADL on the root of your Web API.

http://<API-EndPoint>/help/wadl

For more details, Please refer this Wiki. WADL File Generator in .NET

Pros:

  • It is automated. No manual efforts involved.
  • We can able to see the updated description for the API’s, whenever there is a change in the API code base.

Cons:

  • Dependency on product development team.
  • Dev team may have to upgrade their MVC versions to make this solution to work.

Ready API/SoapUI Rest Discovery:

Soap-UI Pro/Ready API has an inbuilt feature called “Rest Discovery” which will help us to discover the API’s and their descriptions.

Smart Bear has lot of documentation available to guide you step by step about this feature. Have a look at here for Getting Started with Ready API Rest Discovery.

Pros:

  • No dependency on Dev team. Anyone can go ahead and generate the descriptions for their WADL.

Cons:

  • Significant risk of missed API resources – anything not exercised is not recorded in the WADL.
  • Someone from the team should own the responsibility for updating the generated WADL every time, whenever there is a change.
  • Certainly result in a fairly massive performance bottleneck, since all API traffic would be routed to a Soap-UI recorder that is not optimized for performance.

Manually Generating a WADL:

Final option is to write a WADL file manually for your API’s. If you have a good understanding about your API’s internal skeleton like resource, representation, request and response, you can write your own WADL file using any text editor.

Pros:

  • No dependency on Dev team. Anyone can go ahead and generate the descriptions for their WADL.
  • Requires no special tools.

Cons:

  • It involves lot of manual efforts and time consuming.
  • Someone from the team should own the responsibility for updating the generated WADL every time, whenever there is a change.
  • Need to know more about WADL standards and schema.
  • Possible chances for errors which may break the WADL schema.

Ready API/SoapUI Schema Inference:

When creating a REST Service without a WADL, it is often useful to be able to generate these documents anyway, so that validation is made possible, and code/documentation generation tools can be used. Ready! API provides automatic inference of WADL from the model you create in SoapUI, and also inference of XSD schemas from any incoming responses that can be converted to XML, such as XML, JSON and HTML. For more information Please refer Using Inferred Schemas.

Pros:

  • No dependency on Dev team. Anyone can go ahead and generate the descriptions for their WADL.

Cons:

  • Need to know the list of available API’s before inferred with the WADL schema.

I strongly believe that there may be still lot of available solutions to provide a proper documentation for your API. There should be some kind of automated mechanism available for Web API’s in every language like Java, Python etc.

Test Coverage in SoapUI/Ready API:

Test Coverage in the sense, I am referring to the built-in “Contract Coverage” feature in Ready API.

This feature helps us to make sure that we are writing good amount of tests for all the available resources, representations, request and response in an exposed API.

This built-in “Contract Coverage” feature in Ready API is entirely different from the term “Code Coverage” which can be achievable through external tools like Ncover, Cobertura etc.

This Coverage is possible only if we have the proper documentation/WADL provided for the API’s from product teams.

To derive coverage for your API tests, please have a look at this –  Getting started with API Test Coverage

Generate Test Suites using WADL:

If we have the WADL file available at the root of the API URL, our life is going to be much easier by simply importing it into Ready API and auto generating test suites and test cases for all the available resources in your API.

Please have a look at this to learn – How to import WADL into Ready API and auto generate API tests

Additional Note:

If you are APIs are restful, you have a choice to use either “HTTP Request” or “Rest Request” in Ready API. But I recommend using only “Rest Request”.

Please have a look at here – Getting started with Restful Requests

Also for getting API Contract Coverage in Ready API, it is mandatory to use “Rest Request”.

If you have used “HTTP Request” you won’t be able to derive Contract Coverage for your API tests.

SoapUI/Ready API Test Suite and Test Case Count

If we have a very large number of tests in our project, and if you want to calculate the number of test suites/ test cases out of it, simply use the below script as a Groovy Script Test Step in your SoapUI.


def totalTestSuiteCount = 0;
def enabledTestSuiteCount = 0;
def disabledTestSuiteCount = 0;

totalTestSuiteCount = testRunner.testCase.testSuite.project.getTestSuiteCount();

for(i = 0; i < totalTestSuiteCount; i++) {
flag = testRunner.testCase.testSuite.project.getTestSuiteAt(i).isDisabled();
if(!flag)
enabledTestSuiteCount = enabledTestSuiteCount + 1;
else
disabledTestSuiteCount = disabledTestSuiteCount + 1;
}

log.info "Total Test Suite Count : " + totalTestSuiteCount;
log.info "Total Enabled/Active Test Suite Count : " + enabledTestSuiteCount;
log.info "Total Disabled/Reusable Test Suite Count : " + disabledTestSuiteCount;
def totalTestCaseCount = 0;
def enabledTestCaseCount = 0;
def disabledTestCaseCount = 0;

for(i = 0; i < totalTestSuiteCount; i++) {
flag = testRunner.testCase.testSuite.project.getTestSuiteAt(i).isDisabled();
if(!flag) {
totalTestCaseCount = totalTestCaseCount + testRunner.testCase.testSuite.project.getTestSuiteAt(i).getTestCaseCount();
}
}

for(i = 0; i < totalTestSuiteCount; i++) {
flag = testRunner.testCase.testSuite.project.getTestSuiteAt(i).isDisabled();
if(!flag) {
tccount = testRunner.testCase.testSuite.project.getTestSuiteAt(i).getTestCaseCount();
for(j = 0; j < tccount; j++) {
tcflag = testRunner.testCase.testSuite.project.getTestSuiteAt(i).getTestCaseAt(j).isDisabled();
if(!tcflag)
enabledTestCaseCount = enabledTestCaseCount + 1;
else
disabledTestCaseCount = disabledTestCaseCount + 1;
}
}
}

log.info "Total Test Case Count : " + totalTestCaseCount;
log.info "Total Enabled/Active Test Case Count : " + enabledTestCaseCount;
log.info "Total Disabled/Reusable Test Case Count : " + disabledTestCaseCount;

I am here under an impression that, we should exclude the disabled test suites/cases from the count.

Because, in general we used to disable the test suite/case which is not in a proper working condition or if it is acting as a reusable library.

In my case, I have the habit of creating the reusable components/libraries as a test suite/test case and finally I will disable them to exclude those things from normal execution flow.

So I thought, those things should be omitted from the count when taking the statistics.

The next thing I am looking out for is to cover the datasource/datasource loop count also in number of test cases.

Because we are combining so many similar functional test scenarios into one single test case using datasource/datasource loop test steps.

But finally when getting the count, this will also be calculated as one single test case. I don’t want this to be a single test case. If my data source loop is getting executed 10 times, then my test case count also should be 10 instead of 1.

Using Context in Soap-UI/Ready API External Script Library

In the recent post we have seen how to use the external script library to perform a session id extraction from an application.

One interesting thing I recently come across is, we can also use soap-UI context in our external script library.

Context is a built-in variable automatically made available by soapUI.

You can able to define/add your own properties of context objects
For those who are familiar with writing groovy test steps in Soap-UI definitely should heard of this term before.

//An example to set property and get property using soap-UI context.
context.setProperty("URL", "http://www.google.co.in/")
log.info(context.getProperty("URL"))

Lets see how this is going to be useful in our tests.

Let’s consider the same script that I have used before in my previous post
In that, have a look at the code written in “The Script Library to get the SessionID

The method session contains three parameters. URL, Username and Password.

If you are going to call this session method from inside your Soap-UI, you will need to pass the value for these three parameters.

Obviously, Things like URL, Username and Password will be usually stored in a Project Level Property to make it easy for configuration.

So you have to first get these three Project Level Property and then you have to call this external script library.

So if you have a close look at the section “How do we Call this Script inside our SoapUI test cases?” in this Post,

You can able to see that we have extracted the project level property in a variable and then we have used these variables as a parameter in when calling the session method.

def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context );
String url = context.expand( '${#Project#AuthenticateRequestURL}' )
String username = "yourusername";
String password = "yourpassword";
Obj = new Headlessdriver();
String session = Obj.session(url, username, password);

Do we really need to do this every time when we call the external script library? Answer is NO.

We can still use the ‘context’ variable in our external script library also. So I have updated the Headlessdriver.groovy as follows.

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver; 
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.phantomjs.PhantomJSDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriverService;

class Headlessdriver {

	String url;
	String username;
	String password;
	String phantomPath;

	def Headlessdriver( context ) {        
        this.url = context.expand( '${#Project#URL}' );
		this.username = context.expand( '${#Project#username}' );
		this.password = context.expand( '${#Project#password}' );
		this.phantomPath = context.expand( '${#Project#phantomPath}' );
        }

	String session () {

		DesiredCapabilities caps = new DesiredCapabilities();
		// caps.setCapability("phantomjs.binary.path", "ext\\phantomjs.exe");
		caps.setCapability("phantomjs.binary.path", phantomPath);
		caps.setJavascriptEnabled(true);
		String[] args = [ "--ignore-ssl-errors=yes" ];
		caps.setCapability(PhantomJSDriverService.PHANTOMJS_CLI_ARGS, args);
			
		WebDriver driver = new PhantomJSDriver(caps);
			
		driver.manage().timeouts().implicitlyWait(60L, TimeUnit.SECONDS);
		driver.get(url);	
			
		driver.findElement(By.id("username")).clear();
		driver.findElement(By.id("username")).sendKeys(username);
		driver.findElement(By.id("password")).clear();
		driver.findElement(By.id("password")).sendKeys(password);
		driver.findElement(By.cssSelector("input.primary.btn")).click();
			
		String sessionIdFull = driver.manage().getCookieNamed("sessionId").toString();
		String SessionId = sessionIdFull.split("; ")[0];
		driver.quit();
		return SessionId;
	}
}

Now you don’t need to extract the project level property in your groovy script test step before calling this external script library.
Calling this Headlessdriver.session() method is simple as below.

Obj = new Headlessdriver( context );
String session = Obj.session();

SoapUI Married with Selenium

This includes the steps to add up Selenium related libraries to be added to the SOAPUI installation

Step-by-step guide to Setup Selenium Library

  1. Download selenium-server-standalone.jar which includes the Selenium Related libraries.
  2. Copy And paste the downloaded jar file to the SOAPUI installation path, ext folder .
    Path : \ bin \ ext
  3. Download the phantomjs.exe Version-2.0 and Paste the exe file in  \bin\ext directory
  4. Restart Ready-API.

With the above steps, you have added the Selenium library against your SOAPUI installation.

Why do we need a Selenium Library.

  1. There may be instances where we may require to interact with UI to get some data for API automation.
    e.g getting Session-id for automating secure API automation.
  2. Cases to perform end to end testing.

The Script Library to get the SessionID:

I have developed the following Class which will get the the SessionId in headless mode or ghost/daemon mode.

We call it as headless mode or ghost/daemon mode because this program actually runs in the background without a user interface or simply the browser won’t show up when running this test.


import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.phantomjs.PhantomJSDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriverService;
 
class Headlessdriver {
    String session (String url, String username, String password) {
    DesiredCapabilities caps = new DesiredCapabilities();
    caps.setCapability("phantomjs.binary.path", "ext\\phantomjs.exe");
    caps.setJavascriptEnabled(true);
    String[] args = [ "--ignore-ssl-errors=yes" ];
    caps.setCapability(PhantomJSDriverService.PHANTOMJS_CLI_ARGS, args);
     
    //System.setProperty("webdriver.chrome.driver", ".//chromedriver.exe");
    WebDriver driver = new PhantomJSDriver(caps);
    driver.manage().timeouts().implicitlyWait(60L, TimeUnit.SECONDS);
    driver.get(url);   
     
    // Sign in on the CAS login page
    driver.findElement(By.id("username")).clear();
    driver.findElement(By.id("username")).sendKeys(username);
    driver.findElement(By.id("password")).clear();
    driver.findElement(By.id("password")).sendKeys(password);
    driver.findElement(By.cssSelector("input.primary.btn")).click();
     
    String sessionIdFull = driver.manage().getCookieNamed("sessionId").toString();
    String SessionId = sessionIdFull.split("; ")[0];
    driver.quit();
    return SessionId;
    }
}

Save this script as “Headlessdriver.groovy”.

One interesting option in SoapUI is you can define your own script libraries for external groovy scripts.

By default the Script Library points to “\bin\scripts”

You can see that by going to File->Preferences->Ready! API->Script Library

But if you are using source control, then it will be convenient for you to check in the external groovy scripts inside your project.

So what I have done is, I have created a directory called “Scripts” inside my project. Now you can change the above preferences to point the scripts located in your script library.

But in the recent versions, you have to configure this in your Project Level also using relative path.

Click on you “Project” Go to “Project Properties” tab and Change “Resource Root” to “${projectDir}”

Change “Script Library” to “${projectDir}\API-Tests\Scripts”

How do we Call this Script inside our SoapUI test cases?

You can Call this Script from your “Test Case SetUp” or in your “Test Suite SetUp” or from “Groovy Script Test Step”.

def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context );
String url = context.expand( '${#Project#AuthenticateRequestURL}' )
String username = "yourusername";
String password = "yourpassword";
Obj = new Headlessdriver();
String session = Obj.session(url, username, password);
//When Calling from SetUp
testSuite.setPropertyValue("SessionId", session);
//When Calling from Groovy Script Test Step
testRunner.testCase.testSuite.setPropertyValue("SessionId", session); 

The above script will create/update a Property called “SessionId” in the your Test Suite.

You can use this Session information in your header for hitting Secured API’s.

Create a Header Named “Cookie” with Value ${#TestSuite#SessionId} in your SoapUI `http requests.

SoapUI Get SessionID

The whole purpose is, I have to automate testing for some Secured API’s, which need Session Id of my application to send request and to receive expected response.

I am using Soap-UI for my API Testing.

Initially I thought of using SoapUI to achieve the same. But my application uses SAML requests and do lot of redirection before getting into the Login Page.

So my alternative workaround is to go for the some other frameworks to fetch this session Id.

I have already discussed the same in the following post.

https://linkeshkannavelu.com/2014/02/13/selenium-webdriver-get-sessionid-from-a-web-application/

But in the above mentioned Post, I was using the FirefoxDriver. This will trigger the Firefox browser all the time and then it will fetch me the session information.

I somehow felt inside that this is not a proper workaround and always searched for the better alternative solutions.

Now I just want to let you know something that I have tried earlier to get the Session Cookie in headless mode without a browser.

Earlier I have tried out a solution to use the HTMLUnitDriver instead of FirefoxDriver to get the session information with a headless browser.

Initially I got lot of exceptions with earlier version of selenium standalone server.

But this works perfectly with the latest version of selenium. I really don’t know why.

Now the updated script will run in daemon mode and will fetch you the session id without a browser.

Obviously if you use HTMLUnitDriver, it will always throw severe bunch of warnings that you may not need.

All we need is just the session information of the application.

So, somehow I found out a way to turn off the HTMLUnitDriver logging just to avoid these bunch of severe warnings and unwanted information.

The following method will get you the SessionId of a web application.

I just pasted the method here. Of course you guyz may need to do some modification based on your needs.

You need to add the latest version of Selenium-Standalone-Server in your class path for this method to work.

public static void HeadlessSessionId() throws Exception {
		  WebDriver driver = new HtmlUnitDriver(true);
		  
		  try {		  	
			  	LogFactory.getFactory().setAttribute("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.NoOpLog");
			    java.util.logging.Logger.getLogger("com.gargoylesoftware.htmlunit").setLevel(Level.OFF);
			    java.util.logging.Logger.getLogger("org.apache.commons.httpclient").setLevel(Level.OFF);
				String domainString = ServiceEndPoint;		
			    String baseUrl = domainString;
			    driver.manage().timeouts().implicitlyWait(60, TimeUnit.SECONDS);
			    driver.get(baseUrl + "/Demo/");			    
			    driver.findElement(By.id("username")).clear();
			    driver.findElement(By.id("username")).sendKeys("UserName");
			    driver.findElement(By.id("password")).clear();
			    driver.findElement(By.id("password")).sendKeys("PassWord");
			    driver.findElement(By.cssSelector("input.primary.btn")).click();			    		    
			    String SessionId = driver.manage().getCookieNamed("sessionId").toString();			    
			    domainString = domainString.replaceAll("http://", "domain=");
			    String SessionID = ((SessionId).replaceAll("path=/;", " ")).replaceAll(domainString, "").replaceAll("Cookie: ", ""); 
			    Cookie = SessionID;
			    System.out.println(SessionID);
		  }
		  finally {
		    driver.close();
		  }
	  }

So That’s it. We have the session information of our application in Hand. Now how we are going to integrate this with our SoapUI?

During the development phase/when running my tests manually from SoapUI GUI, I have a project level property defined in SoapUI called “Cookie”.

And I will manually replace that project property with the session information that I got from my selenium script.

Later I will use this Project level Property in my SoapUI test cases/ test steps.

But as a automation engineer, at some point, I may want to add these tests in my Continuous Integration Server to run these tests on a nightly Builds.

At that time, there should not be any manual intervention in my automated tests.

So lets see how I actually integrated the session that I am getting it from Selenium with SoapUI.

All we need to do is simple.

Instead of printing the session Id, write it in a property file. I used to write this session Id in “SoapUIProjectProerties.props”

Export the entire Java project into a runnable Jar file.

If you are using any IDE like Eclipse, it is not that difficult task. Then create a Batch file that will do the following steps.

1. First run the Jar File and this will write the SessionID into that Property File.
2. Use SoapUI TestRunner Batch from Command Line and Specify it to load the Properties from the “SoapUIProjectProerties.props”

@ECHO OFF
SET SOAPUI_TEST_RUNNER="C:\Program Files (x86)\SmartBear\SoapUI-5.0.0\bin\"
java -jar HeadlessSessionId.jar
%SOAPUI_TEST_RUNNER%testrunner.bat -sTestSuiteName -r -a -j -f"Reports" -i Project-Name.xml 
-Dsoapui.properties.CommonAuthWebService=SoapUIProjectProerties.props

Now during the runtime, the session Id property defined in the property file will be used by SoapUI for hitting secured API’s.

I hope this solution might be helpful for your when automating secured API’s which need session Id.

My Colleague is working on another interesting solution to rewrite the whole Java program as a ‘Groovy Test Script’ in SoapUI.

I will let you know if that works. Thanks.

SoapUI – Properties using Groovy

//*CODE STARTS
//To GET THE TIME STAMP IN DIFFERENT FORMATS
//*
 
def sdf = new java.text.SimpleDateFormat("yyyy-MM-dd hh:mm:ss")
log.info (sdf.format( new Date()))
 
def dff = new java.text.SimpleDateFormat("yyyy-dd-MM hh:mm:ss")
log.info (dff.format( new Date()))
 
def dtt = new java.text.SimpleDateFormat("yyyy-dd-MM")
log.info (dtt.format( new Date()))
 
 
//*
//TO GET THE DEFAULT PROPERTIES FROM SOAP UI
//*
 
 
 
//TO get the Project Name
def username = testRunner.testCase.testSuite.project.name
log.info (username)
 
//To get the Test Suite Name
def username1 = testRunner.testCase.testSuite.name
log.info (username1)
 
//TO get the Test Case Name
def username2 = testRunner.testCase.name
log.info (username2)
 
//TO get the Test Step Name
def username3 = context.getCurrentStep().getLabel()
log.info (username3)
 
 
 
//*
//To GET AND SET CUSTOM PROPERTIES IN SOAP UI
//*
 
 
 
//TO SET THE CUSTOM TEST CASE PROPERTY
testRunner.testCase.setPropertyValue( "MyProp", 'someValue' )
 
//TO GET THE CUSTOM PROPERTY THAT THE USER HAS CREATED IN TEST CASE
def testCaseProperty = testRunner.testCase.getPropertyValue( "MyProp" )
log.info(testCaseProperty)
 
//TO SET THE CUSTOM TEST SUITE PROPERTY
testRunner.testCase.testSuite.setPropertyValue( "MyProp", 'someValue' )
 
//TO GET THE CUSTOM PROPERTY THAT THE USER HAS CREATED IN TEST SUITE
def testSuiteProperty = testRunner.testCase.testSuite.getPropertyValue( "MyProp" )
log.info(testSuiteProperty)
 
//TO SET THE CUSTOM PROJECT PROPERTY
testRunner.testCase.testSuite.project.setPropertyValue( "MyProp", 'someValue' )
 
//TO GET THE CUSTOM PROPERTY THAT THE USER HAS CREATED IN PROJECT
def projectProperty = testRunner.testCase.testSuite.project.getPropertyValue( "MyProp" )
log.info(projectProperty)
 
//CODE ENDS HERE