Integrate Micro Focus ALM with Perfecto
Many organizations are eager to utilize Selenium-based tests for both web and mobile testing but still require the test management capabilities of Micro Focus ALM. This reference architecture describes a method for executing Selenium-based scripts from Micro Focus ALM. You can easily modify it to run on concurrent devices, use a different build server, or use a different unit test framework.
On this page:
Prerequisites
This section assumes that you have a basic understanding of the following:
- Micro Focus ALM customization
- Jenkins configuration
- Selenium coding in Java using Eclipse
In addition, setting up the Perfecto integration with Micro Focus ALM requires the following:
-
Micro Focus ALM 11.0 or later
The project template provided here is for ALM 17.
-
Jenkins CI Server (or equivalent)
-
Source code repository supported by Jenkins (this example uses Subversion)
-
Java Selenium-based tests leveraging the JUnit framework
-
A Java project that uses Maven for build dependencies
Implementation overview
Micro Focus ALM provides the following extensible APIs for integrating external tools:
- OTA (Open Test Architecture)
- VAPI-XP tests
This implementation uses a standard VAPI-XP test in Micro Focus ALM. When the test is executed, parameters are first passed into the test, then the Jenkins API is called to initiate an execution of a predefined build job. This build job executes a Selenium-based test. When the build job has finished, the VAPI-XP test collects the console output from Jenkins and the Perfecto execution report and attaches them to the run.
Step-by-step instructions
Click a step to view details.
The attached ZIP file (ALM-Selenium.zip) is an export of an Eclipse project that is configured with Maven. This project leverages TestNg-based tests. All tests are stored in the src/test/tests
directory of the project. To interact with the Perfecto cloud, you must provide a Perfecto security token, Reportium Job Name, Reportium Job Number, and the Perfecto cloud URL. These parameters are passed into the test via a system property that is provided in Jenkins.
String host = System.getProperty("PerfectoCloud");
String securityToken = System.getProperty("PerfectoSecurityToken");
String reportiumJobName = System.getProperty("reportium-job-name");
String Dreportium_job_number = System.getProperty("reportium-job-number");
Each .java
file in the src/test/tests
folder constitutes a single Selenium test. This test is known as the ClassPath. Make sure to store the project in a source code management system (such as Subversion, CVS, or Git).
-
Determine your Jenkins security model. Your Jenkins server either requires a login or allows anonymous usage.
-
Create a new Maven project.
-
Create the following build parameters:
Parameter name
Type
PERFECTOSECURITYTOKEN
String
PERFECTOCLOUD
String
REPPORTIUMJOBNAME
String
REPPORTIUMJOBNUMBER
String
-
Configure the project to connect to your SCM.
-
Configure the build process by providing the following parameters:
-
Root POM:
pom.xml
-
Goals and options:
Copytest -Dtest=$CLASSPATH -DPerfectoDeviceID=$PERFECTODEVICEID -DPerfectoUsername=$PERFECTOUSERNAME -DPerfectoPassword=$PERFECTOPASSWORD -DPerfectoCloud=$PERFECTOCLOUD
-
-
If your Jenkins implementation requires authentication, get the API token of the user by opening the user profile, click the Configure link, and then click Show API Token.
Follow these steps to configure your Micro Focus ALM instance. A template ALM project is attached for reference: alm-17-selenium-project-template.qcp
' VAPI-XP Template [VBScript]
' ====================================================
Option explicit
' ----------------------------------------------------
' Main Test Function
' Debug - Boolean. Equals to false if running in [Test Mode] : reporting to Application Lifecycle Management
' CurrentTestSet - [OTA COM Library].TestSet.
' CurrentTSTest - [OTA COM Library].TSTest.
' CurrentRun - [OTA COM Library].Run.
' ----------------------------------------------------
Const PERFECTO_CONFIG = "Perfecto Mobile"Const JENKINS_JAVA_CLASSPATH_FIELD = "TS_USER_01"Const JENKINS_JOB_NAME_FIELD = "TS_USER_02"
Const DEBUG_JOB = "Test1"Const DEBUG_CLASSPATH = "Demo Test"
Const DEBUG_DEVICE_ID = "************"
Sub Test_Main(Debug, CurrentTestSet, CurrentTSTest, CurrentRun)
' *** VBScript Limitation ! ***
' "On Error Resume Next" statement suppresses run-time script errors.
' To handle run-time error in a right way, you need to put "If Err.Number <> 0 Then" ' after each line of code that can cause such a run-time error.
On Error Resume Next
' clear output window
TDOutput.Clear
Dim username, password, cloudURL, jobName, classPath, jenkinsURL, jenkinsAuth, jenkinsUsername, jenkinsAPIToken, authString, deviceID
username = TDConnection.Customization.Lists.List(PERFECTO_CONFIG).RootNode.Child("Perfecto Automation Username").Children.Item(1).Name
password = TDConnection.Customization.Lists.List(PERFECTO_CONFIG).RootNode.Child("Perfecto Automation Password").Children.Item(1).Name
cloudURL = TDConnection.Customization.Lists.List(PERFECTO_CONFIG).RootNode.Child("Cloud Host Name").Children.Item(1).Name
jenkinsURL = TDConnection.Customization.Lists.List(PERFECTO_CONFIG).RootNode.Child("Jenkins URL").Children.Item(1).Name
jenkinsAuth = TDConnection.Customization.Lists.List(PERFECTO_CONFIG).RootNode.Child("Jenkins Authentication").Children.Item(1).Name
If jenkinsAuth Then
jenkinsUsername = TDConnection.Customization.Lists.List(PERFECTO_CONFIG).RootNode.Child("Jenkins Username").Children.Item(1).Name
jenkinsAPIToken = TDConnection.Customization.Lists.List(PERFECTO_CONFIG).RootNode.Child("Jenkins API Token").Children.Item(1).Name
authString = "Basic " & Base64Encode(jenkinsUsername & ":" & jenkinsAPIToken)
End If
If Not Debug Then
'Grab the device ID from the actual value of the Test Configuration DeviceID parameter
Dim paramValueFct, paramList
Set paramValueFct = CurrentTSTest.ParameterValueFactory
Set paramList = paramValueFct.NewList("")
deviceID = Trim(RemoveHTML(paramList.Item(1).ActualValue))
deviceID = Replace(deviceID, vbCrLf, "")
jobName = TDConnection.TestFactory.Item(CurrentRun.TestId).Field(JENKINS_JOB_NAME_FIELD)
classPath = TDConnection.TestFactory.Item(CurrentRun.TestId).Field(JENKINS_JAVA_CLASSPATH_FIELD)
Else
deviceID = DEBUG_DEVICE_ID
jobName = DEBUG_JOB
classPath = DEBUG_CLASSPATH
End If
dim restReq, xmlResponse, buildNumberXML
set restReq = CreateObject("MSXML2.ServerXMLHTTP.6.0")
Set xmlResponse = CreateObject("MSXML2.DOMDocument.6.0")
Dim reqURL
reqURL = jenkinsURL & "job/" & jobName & "/buildWithParameters"
TDOutput.Print "EXECUTING BUILD:" TDOutput.Print reqURL
If jenkinsAuth Then
restReq.open "POST", reqURL, False, jenkinsUsername, jenkinsAPIToken
restReq.setRequestHeader "Authorization", authString
Else
restReq.open "POST", reqURL
End If
restReq.setRequestHeader "Content-Type", "application/x-www-form-urlencoded" restReq.send "CLASSPATH=" & classPath & "&PERFECTODEVICEID=" & deviceID & "&PERFECTOUSERNAME=" & username & "&PERFECTOPASSWORD=" & password & "&PERFECTOCLOUD=" & cloudURL
Dim locationURL
locationURL = restReq.getResponseHeader("Location")
'TDOutput.Print locationURL
reqURL = locationURL & "api/xml"
'get the job URL
Dim consoleTextPath
Dim buildNodes
TDOutput.Print "WAITING FOR EXECUTION TO BEGIN"
'Refresh the queue until the execution begins
Do
If jenkinsAuth Then
restReq.open "GET", reqURL, False, jenkinsUsername, jenkinsAPIToken
restReq.setRequestHeader "Authorization", authString
Else
restReq.open "GET", reqURL
End If
restReq.send
'TDOutput.Print restReq.responseText
xmlResponse.loadXML restReq.responseText
xmlResponse.setProperty "SelectionLanguage", "XPath"
Set buildNodes = xmlResponse.selectNodes ("//executable/number")
XTools.Sleep 1000
'TDOutput.Print buildNodes.length
Loop While buildNodes.length = 0
'Set buildNumberXML = xmlResponse.selectSingleNode ("//executable/number")
Dim buildNumber
buildNumber = buildNodes.item(0).text
TDOutput.Print "CREATING BUILD: " & buildNumber
TDOutput.Print "EXECUTING TEST: " & classPath
'wait For execution To complete
reqURL = jenkinsURL & "/job/" & jobName & "/" & buildNumber & "/api/xml"
Dim buildingNode
Do
If jenkinsAuth Then
restReq.open "GET", reqURL, False, jenkinsUsername, jenkinsAPIToken
restReq.setRequestHeader "Authorization", authString
Else
restReq.open "GET", reqURL
End If
restReq.send
xmlResponse.loadXML restReq.responseText
xmlResponse.setProperty "SelectionLanguage", "XPath"
Set buildingNode = xmlResponse.selectSingleNode ("//building")
XTools.Sleep 1000
Loop While buildingNode.text = "true" Dim resultXML
Set resultXML = xmlResponse.selectSingleNode ("//failCount")
TDOutput.Print xmlResponse.xml
TDOutput.Print "Failed Steps: " & resultXML.text
If Int(resultXML.text) <> 0 Then
TDOutput.Print "Test Failed" If Not Debug Then
CurrentRun.Status = "Failed" CurrentTSTest.Status = "Failed" End If
Else
TDOutput.Print "Test Passed" If Not Debug Then
CurrentRun.Status = "Passed" CurrentTSTest.Status = "Passed" End If
End If
consoleTextPath = jenkinsURL & "job/" & jobName & "/" & buildNumber & "/consoleText" 'TDOutput.Print consoleTextPath
If jenkinsAuth Then
restReq.open "GET", consoleTextPath, False, jenkinsUsername, jenkinsAPIToken
restReq.setRequestHeader "Authorization", authString
Else
restReq.open "GET", consoleTextPath
End If
restReq.send
'TDOutput.Print restReq.responseText
'Find the execution information from the console log
Dim consoleLines, xmlString, consoleText
consoleText = restReq.responseText
consoleLines = Split(restReq.responseText, vbCrLf)
Dim consoleLine, executionLine
For Each consoleLine In consoleLines
TDOutput.Print consoleLine
If InStr (consoleLine, "<?xml") > 0 Then
xmlString = Mid(consoleLine, InStr (consoleLine, "<?xml"), Len(consoleLine) - InStr (consoleLine, "<?xml") + 1)
'TDOutput.Print xmlString
Exit For
End If
Next
Dim executionXML
Set executionXML = CreateObject("MSXML2.DOMDocument.6.0")
executionXML.loadXML xmlString
Dim reportKeyNode
executionXML.setProperty "SelectionLanguage", "XPath" Set reportKeyNode = executionXML.selectSingleNode ("//reportKey")
TDOutput.Print "EXECUTION FINISHED" TDOutput.Print "DOWNLOADING EXECUTION REPORT"
If Not Debug Then
'Download the execution report and attach it to the run
restReq.open "GET", "https://" & cloudURL & "/services/reports/" & reportKeyNode.text & "?operation=download&user=" & username & "&password=" & password & "&format=html" restReq.send
Dim fso, file, path
Set fso = CreateObject("Scripting.FileSystemObject")
path = fso.GetSpecialFolder(2)
path = path & "\" & Replace(Replace(Replace(reportKeyNode.text, "xml", "html"), "/", "_"), ":", "_")
Set file = fso.CreateTextFile( path, True)
file.Write restReq.responseText
file.Close
Dim objRunAttach, newAttach
Set objRunAttach = CurrentRun.Attachments
Set newAttach = objRunAttach.AddItem (Null)
newAttach.FileName = path
newAttach.Type = 1
newAttach.Post
path = fso.GetSpecialFolder(2) & "\RUN_" & CurrentRun.ID & "_" & classPath & "_console_log.txt"
Set file = fso.CreateTextFile (path , True)
file.Write consoleText
file.Close
Dim logAttach
Set logAttach = objRunAttach.AddItem(Null)
logAttach.FileName = path
logAttach.Type = 1
logAttach.Post
'Download the execution XML report, parse it, and add the steps to the run report
'WScript.Echo "https://" & cloudURL & "/services/reports/" & reportKey & "?operation=download&user=" & username & "&password=" & password & "&format=xml" restReq.open "GET", "https://" & cloudURL & "/services/reports/" & reportKeyNode.text & "?operation=download&user=" & username & "&password=" & password & "&format=xml" restReq.send
'WScript.Echo restReq.responseText
xmlResponse.loadXML restReq.responseText
xmlResponse.setProperty "SelectionLanguage", "XPath"
Dim actionList
Set actionList = xmlResponse.selectNodes ("//action")
Dim action, status
For Each action In actionList
'WScript.Echo action.childNodes.item(0).firstChild.attributes.getNamedItem("displayName").text
If action.childNodes.item(1).firstChild.firstChild.text Then
status = "Passed" Else
status = "Failed" End If
TDHelper.AddStepToRun action.childNodes.item(0).firstChild.attributes.getNamedItem("displayName").text, , , , status
Next
End If
'handle run-time errors
If Err.Number <> 0 Then
TDOutput.Print "Run-time error [" & Err.Number & "] : " & Err.Description
'update execution status in "Test" mode
If Not Debug Then
CurrentRun.Status = "Failed" CurrentTSTest.Status = "Failed" End If
End If
End Sub
Function Base64Encode(inData)
'rfc1521
'2001 Antonin Foller, Motobit Software, http://Motobit.cz
Const Base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" Dim cOut, sOut, I
'For each group of 3 bytes
For I = 1 To Len(inData) Step 3
Dim nGroup, pOut, sGroup
'Create one long from this 3 bytes.
nGroup = &H10000 * Asc(Mid(inData, I, 1)) + _
&H100 * MyASC(Mid(inData, I + 1, 1)) + MyASC(Mid(inData, I + 2, 1))
'Oct splits the long To 8 groups with 3 bits
nGroup = Oct(nGroup)
'Add leading zeros
nGroup = String(8 - Len(nGroup), "0") & nGroup
'Convert To base64
pOut = Mid(Base64, CLng("&o" & Mid(nGroup, 1, 2)) + 1, 1) + _
Mid(Base64, CLng("&o" & Mid(nGroup, 3, 2)) + 1, 1) + _
Mid(Base64, CLng("&o" & Mid(nGroup, 5, 2)) + 1, 1) + _
Mid(Base64, CLng("&o" & Mid(nGroup, 7, 2)) + 1, 1)
'Add the part To OutPut string
sOut = sOut + pOut
'Add a new line For Each 76 chars In dest (76*3/4 = 57)
'If (I + 2) Mod 57 = 0 Then sOut = sOut + vbCrLf
Next
Select Case Len(inData) Mod 3
Case 1: '8 bit final
sOut = Left(sOut, Len(sOut) - 2) + "==" Case 2: '16 bit final
sOut = Left(sOut, Len(sOut) - 1) + "=" End Select
Base64Encode = sOut
End Function
Function MyASC(OneChar)
If OneChar = "" Then MyASC = 0 Else MyASC = Asc(OneChar)
End Function
Function RemoveHTML( strText )
Dim TAGLIST
TAGLIST = ";!--;!DOCTYPE;A;ACRONYM;ADDRESS;APPLET;AREA;B;BASE;BASEFONT;" &_
"BGSOUND;BIG;BLOCKQUOTE;BODY;BR;BUTTON;CAPTION;CENTER;CITE;CODE;" &_
"COL;COLGROUP;COMMENT;DD;DEL;DFN;DIR;DIV;DL;DT;EM;EMBED;FIELDSET;" &_
"FONT;FORM;FRAME;FRAMESET;HEAD;H1;H2;H3;H4;H5;H6;HR;HTML;I;IFRAME;IMG;" &_
"INPUT;INS;ISINDEX;KBD;LABEL;LAYER;LAGEND;LI;LINK;LISTING;MAP;MARQUEE;" &_
"MENU;META;NOBR;NOFRAMES;NOSCRIPT;OBJECT;OL;OPTION;P;PARAM;PLAINTEXT;" &_
"PRE;Q;S;SAMP;SCRIPT;SELECT;SMALL;SPAN;STRIKE;STRONG;STYLE;SUB;SUP;" &_
"TABLE;TBODY;TD;TEXTAREA;TFOOT;TH;THEAD;TITLE;TR;TT;U;UL;VAR;WBR;XMP;" Const BLOCKTAGLIST = ";APPLET;EMBED;FRAMESET;HEAD;NOFRAMES;NOSCRIPT;OBJECT;SCRIPT;STYLE;"
Dim nPos1
Dim nPos2
Dim nPos3
Dim strResult
Dim strTagName
Dim bRemove
Dim bSearchForBlock
nPos1 = InStr(strText, "<")
Do While nPos1 > 0
nPos2 = InStr(nPos1 + 1, strText, ">")
If nPos2 > 0 Then
strTagName = Mid(strText, nPos1 + 1, nPos2 - nPos1 - 1)
strTagName = Replace(Replace(strTagName, vbCr, " "), vbLf, " ")
nPos3 = InStr(strTagName, " ")
If nPos3 > 0 Then
strTagName = Left(strTagName, nPos3 - 1)
End If
If Left(strTagName, 1) = "/" Then
strTagName = Mid(strTagName, 2)
bSearchForBlock = False
Else
bSearchForBlock = True
End If
If InStr(1, TAGLIST, ";" & strTagName & ";", vbTextCompare) > 0 Then
bRemove = True
If bSearchForBlock Then
If InStr(1, BLOCKTAGLIST, ";" & strTagName & ";", vbTextCompare) > 0 Then
nPos2 = Len(strText)
nPos3 = InStr(nPos1 + 1, strText, "</" & strTagName, vbTextCompare)
If nPos3 > 0 Then
nPos3 = InStr(nPos3 + 1, strText, ">")
End If
If nPos3 > 0 Then
nPos2 = nPos3
End If
End If
End If
Else
bRemove = False
End If
If bRemove Then
strResult = strResult & Left(strText, nPos1 - 1)
strText = Mid(strText, nPos2 + 1)
Else
strResult = strResult & Left(strText, nPos1)
strText = Mid(strText, nPos1 + 1)
End If
Else
strResult = strResult & strText
strText = "" End If
nPos1 = InStr(strText, "<")
Loop
strResult = strResult & strText
RemoveHTML = strResult
End Function
-
In the ALM project, open the project customization.
-
Open the Project Lists category.
-
Select New List and name the list
Perfecto Mobile
. -
Create the following items and sub-items.
Item
Sub-Item Value
Cloud Host Name
The hostname in the format:
mycompany.perfectomobile.com
Jenkins API Token
The API token value as found in Jenkins (only applicable when Jenkins Authentication = True)
Jenkins Authentication
If your Jenkins server requires authentication, provide the value
True
. Otherwise, provide the valueFalse
.Jenkins URL
The full URL of the Jenkins server with trailing slash. Example:
http://myjenkins.mycompany.com:8080/
Jenkins Username
The username used for Jenkins (only applicable when Jenkins Authentication = True)
Perfecto Security Token
The Perfecto security token that will be used for automation. For more information, see Generate security tokens.
-
Select New List and name the list
Jenkins Job Names
. -
Create a new item for each Jenkins job you have created (in this example, you only need one).
-
Save the Project Lists category.
-
Open the Project Entities category and expand the Test entity.
-
Create a new User Field named
Test Class Name
and provide the following configuration options.Type
String
Length
255
Sanitazation Type (ALM 12+)
Text
Masked
Off
Take note of the Name field. It will contain a value in the format TS_USER_XX, where
XX
is a two-digit integer. -
Create a new User field named
Jenkins Job
and provide the following configuration optionsType
Lookup List
Lookup List
Jenkins Job Names
Verify value
Checked
Allow Multiple Values
Not Checked
Take note of the Name field. It will contain a value in the format TS_USER_XX, where
XX
is a two-digit integer. - Save the customization changes and return to the project.
- Open the Test Plan module and create a new test. Choose VAPI-XP as the Test Type. This test will become the template for new Selenium tests going forward.
- Open the Script tab of the newly created test case and copy the below script into the editor.
- Edit the Const values
JENKINS_JAVA_CLASSPATH_FIELD
andJENKINS_JOB_NAME_FIELD
to reflect the field names that were created in steps 9 and 10. - Open the Parameters tab and create a new parameter named
DeviceID
.
- Create new Selenium tests in the Java project.
- Commit all code changes to your source code repository.
- Copy the VAPI-XP template test in Micro Focus ALM and name it appropriately.
- Select the Jenkins build job name that applies to your test. (Each Jenkins build job can only map to one source code repository. If you require the use of multiple code repositories, a different job would be needed to execute the tests.)
- Provide the Test Class Name of your test in the Test Class Name field. This will be your Java class name. For example, if you have a file named
mySeleniumTest.java
, your class name would bemySeleniumTest
. - Open the Test Configurations tab and create a configuration for each device you wish to run the test on. In the Data tab of the configuration, provide the Perfecto Device ID in the Actual Value field.
- Add the tests or test configurations to a test set. They are now ready for execution. All test results will be attached to the run for each test instance.