Gmail OAuth 2.0 API Automation Example and example SubmitListener.beforeSubmit
Hi. Recently I had a use case where I had to verify that our application sends an email in a particular case. In my case, it was an email whenever a certain status is reached, but it could also be for instance the sending of an invitation (user-creation) email or whatnot. In essence: I want to check at a give moment when the status condition is reached that the email is delivered to the proper email address. In the past, there we some free email generators (like mailinator, 10minutemail,...) where you could get API calls to check the inbox, but I couldn't find any that offered that feature for free. So I decided to setup a dedicated gmail email address for my tests and talk to the gmail api to read the inbox (all messages), verify the subject and the actual content of a mail and delete the messages. More info on the gmail api here: https://developers.google.com/gmail/api/reference/rest. To get started, I created a gmail user and setup an OAuth2.0 google client Id (https://console.cloud.google.com/apis/credentials). These settings (clientId and secret) I used to setup an Authorization code grant as described here:https://support.smartbear.com/readyapi/docs/requests/auth/types/oauth2/grants/auth-code.html So far so good, the readyAPI internal browers showed me the google popup and I could manually insert the authentication that was needed to generate successfully an access token from google. BUT: When I tried to automate the flow in this popup using the example code provided in the documentation (=https://support.smartbear.com/readyapi/docs/requests/auth/types/oauth2/automate/sample.html) it did not work for me. Therefore I wanted to share the changes I made to it in order to get it working. Also the event handler SubmitListener.beforeSubmit(described on the same page) needed some rework for me to work properly. As an extra, I also have a test case setup script that deletes all emails in the gmail inbox so I have a proper starting situation for my tests. Hope this can help any other testers that would need this! Automation Scripts tab of the Auth Manager I have encrypted project properties that store my gmail username (gmailUser) and password (gmailPass). Page 1: // This function asks for permission to use OAuth. The user must be logged in to use it. Logging in is performed in the script below. function consent() { if (document.getElementById('submit_approve_access')){ document.getElementById('submit_approve_access').click(); } } // This function fills user password in when the user name is already known. It uses the project-level "pass" property. function fillpwd() { document.getElementsByName('password')[0].value = '${#Project#gmailPass}'; document.getElementById('passwordNext').click(); window.setInterval(consent, 1000); } // This script checks what page is displayed and provides the appropriate data. It uses the project-level "user" and "pass" properties. if (document.getElementById('profileIdentifier')) { document.getElementById('profileIdentifier').click(); window.setTimeout(fillpwd, 1000) }else if (document.getElementById('identifierId') && document.getElementById('identifierNext')) { document.getElementById('identifierId').value = '${#Project#gmailUser}'; document.getElementById('identifierNext').click(); window.setTimeout(fillpwd, 1000); } else if (document.getElementByType('password')) { fillpwd(); } else if(document.getElementById('submit_approve_access')){ window.setInterval(consent, 100); } Page 2: function consent() { if (document.getElementById('submit_approve_access')){ document.getElementById('submit_approve_access').click(); } } window.setInterval(consent, 100); Event handler SubmitListener.beforeSubmit: Note: There might be some redundant iteration of code in there, feel free to rewrite, main thing is: it works. I also expected that I could use the "Target" column to filter on the requests steps that start with "gmail*" but that didn't do it. So I fixed that in another way, together with providing some smart checking whether a new token generation is needed or not (gmail token is valid for 60 minutes). // Import the required classes import com.eviware.soapui.impl.rest.actions.oauth.OltuOAuth2ClientFacade; import com.eviware.soapui.support.editor.inspectors.auth.TokenType; import com.eviware.soapui.model.support.ModelSupport; import java.time.LocalDateTime import java.time.format.DateTimeFormatter def testStepName = context.getModelItem().getName() def expiresOn = context.expand('${#Project#expiresOn}') if (testStepName.toLowerCase().contains("gmail")) { // IF expiresOn == "" OR dateNow is > expiresOn then we need to get a new token. Otherwise the old should still do.... TimeZone.setDefault(TimeZone.getTimeZone('UTC')) TimeZone tz = TimeZone.getTimeZone("UTC") LocalDateTime dateNow = LocalDateTime.now() def patternUTC = "yyyy-MM-dd\'T\'HH:mm:ss.SSS\'Z\'" DateTimeFormatter dateFormatUTC = DateTimeFormatter.ofPattern(patternUTC).withLocale(Locale.US) String nowUtcFormat = dateNow.format(dateFormatUTC) LocalDateTime dateExpiresOn = dateNow.plusMinutes(55) String expiresOnUtcFormat = dateExpiresOn.format(dateFormatUTC) if (expiresOn == "") { /* log.info("Expires on is empty, so we need to run submitListener and get new token. We also write the new expiresOn to the project properties!") log.info("nowUtcFormat = " + nowUtcFormat) log.info("dateExpiresOn = " + expiresOnUtcFormat) log.info "let's run the submitListener.beforeSubmit to get a new accessToken. We know this one will be valid for 60 minutes, so we set a the expiresOn project property to now + 55 minutes" */ // Set up variables def project = ModelSupport.getModelItemProject(context.getModelItem()) project.setPropertyValue("expiresOn", expiresOnUtcFormat) def authProfile = project.getAuthRepository().getEntry("google") def oldToken = authProfile.getAccessToken() def tokenType = TokenType.ACCESS // Create a facade object def oAuthFacade = new OltuOAuth2ClientFacade(tokenType) // Request an access token in headless mode oAuthFacade.requestAccessToken(authProfile, true, true) // Wait until the access token gets updated int iteration = 0 while (oldToken == authProfile.getAccessToken() && iteration < 10) { sleep(500) iteration++ } // Post the info to the log log.info("Gmail authentication event handler: Project property \"expiresOn\" is empty! We get/set new token: " + authProfile.getAccessToken() + " with new expiresOn = " + expiresOnUtcFormat + ". The old token (with unknown expiresOn) was = " + oldToken) } else { def potentialNewExpiresOn = expiresOnUtcFormat //expiresOn retrieved from project properties dateExpiresOn = LocalDateTime.parse(expiresOn, dateFormatUTC) expiresOnUtcFormat = dateExpiresOn.format(dateFormatUTC) //log.info("dateExpiresOn = " + expiresOnUtcFormat) if (dateNow > dateExpiresOn) { // log.info "Expired! Let's get a new token..." // Set up variables def project = ModelSupport.getModelItemProject(context.getModelItem()) def authProfile = project.getAuthRepository().getEntry("google") def oldToken = authProfile.getAccessToken() def tokenType = TokenType.ACCESS // Create a facade object def oAuthFacade = new OltuOAuth2ClientFacade(tokenType) // Request an access token in headless mode oAuthFacade.requestAccessToken(authProfile, true, true) // Wait until the access token gets updated int iteration = 0 while (oldToken == authProfile.getAccessToken() && iteration < 10) { sleep(500) iteration++ } // Post the info to the log project.setPropertyValue("expiresOn", potentialNewExpiresOn) log.info("Token was expired! We get/set new token: " + authProfile.getAccessToken() + " with new expiresOn = " + potentialNewExpiresOn + ". The old token (expiresOn = $expiresOn vs now " + nowUtcFormat + ") was = " + oldToken) } else { //log.info "Not yet expired. Let's keep using the same old token (expiresOn = $expiresOn vs now " + nowUtcFormat + ")" } } } Setup script to delete all emails in the gmail inbox for proper start situation: I have a disabled test suite "WorkItem" with a test case named "GmailStartSituationCleanup" This test case has 4 steps: 1°GET gmail messagesList 2° Script "IterateOverAllGmailMessageIds" 3°DELETE gmail messageId 4°GET gmail messagesList-EmptyListCheck def testSuiteWorkItem = testRunner.testCase.testSuite.project.getTestSuiteByName("WorkItem") def testCaseGmailStartSituationCleanup = testSuiteWorkItem.getTestCaseByName("GmailStartSituationCleanup") testCaseGmailStartSituationCleanup.run(new com.eviware.soapui.support.types.StringToObjectMap(), false) The groovy test step 2°IterateOverAllGmailMessageIds = import com.eviware.soapui.support.JsonUtil def testStepAllMessages = testRunner.testCase.getTestStepAt(context.getCurrentStepIndex()-1) def teststepNameAllMessages = testStepAllMessages.getName() def testStepDeleteMessage = testRunner.testCase.getTestStepAt(context.getCurrentStepIndex()+1) testStepDeleteMessage.setDisabled(true) def testStepVerifAllDeleted = testRunner.testCase.getTestStepAt(context.getCurrentStepIndex()+2) testStepVerifAllDeleted.setDisabled(false) def responseStatus = testStepAllMessages.testRequest.response.responseHeaders["#status#"][0] if (responseStatus.contains("HTTP/1.1 2")){ def responseMessages = context.expand( '${'+teststepNameAllMessages+'#Response#$[\'messages\']}' ) if (responseMessages!= "" && responseMessages!= null){ def numberOfMessages = (JsonUtil.parseTrimmedText(responseMessages)).size() def id for (i=0;i<numberOfMessages;i++){ id = context.expand( '${'+teststepNameAllMessages+'#Response#$[\'messages\']['+i+'][\'id\']}' ) testStepDeleteMessage.setPropertyValue("messageId", id) log.info "Cleanup of gmail messages : Message "+(i+1).toString()+"/"+numberOfMessages.toString()+" with messageId $id will be deleted so we can continue with a proper starting situation..." testStepDeleteMessage.run(testRunner, context) } }else{ log.info "Cleanup of gmail messages : No messages found. We can continue with proper starting situation" testStepVerifAllDeleted.setDisabled(true) } }1.2KViews5likes2CommentsGmail OAuth 2.0 API Automation Example and example SubmitListener.beforeSubmit
Hi. Recently I had a use case where I had to verify that our application sends an email in a particular case. In my case, it was an email whenever a certain status is reached, but it could also be for instance the sending of an invitation (user-creation) email or whatnot. In essence: I want to check at a give moment when the status condition is reached that the email is delivered to the proper email address. In the past, there we some free email generators (like mailinator, 10minutemail,...) where you could get API calls to check the inbox, but I couldn't find any that offered that feature for free. So I decided to setup a dedicated gmail email address for my tests and talk to the gmail api to read the inbox (all messages), verify the subject and the actual content of a mail and delete the messages. More info on the gmail api here: https://developers.google.com/gmail/api/reference/rest. To get started, I created a gmail user and setup an OAuth2.0 google client Id (https://console.cloud.google.com/apis/credentials). These settings (clientId and secret) I used to setup an Authorization code grant as described here:https://support.smartbear.com/readyapi/docs/requests/auth/types/oauth2/grants/auth-code.html So far so good, the readyAPI internal browers showed me the google popup and I could manually insert the authentication that was needed to generate successfully an access token from google. BUT: When I tried to automate the flow in this popup using the example code provided in the documentation (=https://support.smartbear.com/readyapi/docs/requests/auth/types/oauth2/automate/sample.html) it did not work for me. Therefore I wanted to share the changes I made to it in order to get it working. Also the event handler SubmitListener.beforeSubmit(described on the same page) needed some rework for me to work properly. As an extra, I also have a test case setup script that deletes all emails in the gmail inbox so I have a proper starting situation for my tests. Hope this can help any other testers that would need this! Automation Scripts tab of the Auth Manager I have encrypted project properties that store my gmail username (gmailUser) and password (gmailPass). Page 1: // This function asks for permission to use OAuth. The user must be logged in to use it. Logging in is performed in the script below. function consent() { if (document.getElementById('submit_approve_access')){ document.getElementById('submit_approve_access').click(); } } // This function fills user password in when the user name is already known. It uses the project-level "pass" property. function fillpwd() { document.getElementsByName('password')[0].value = '${#Project#gmailPass}'; document.getElementById('passwordNext').click(); window.setInterval(consent, 1000); } // This script checks what page is displayed and provides the appropriate data. It uses the project-level "user" and "pass" properties. if (document.getElementById('profileIdentifier')) { document.getElementById('profileIdentifier').click(); window.setTimeout(fillpwd, 1000) }else if (document.getElementById('identifierId') && document.getElementById('identifierNext')) { document.getElementById('identifierId').value = '${#Project#gmailUser}'; document.getElementById('identifierNext').click(); window.setTimeout(fillpwd, 1000); } else if (document.getElementByType('password')) { fillpwd(); } else if(document.getElementById('submit_approve_access')){ window.setInterval(consent, 100); } Page 2: function consent() { if (document.getElementById('submit_approve_access')){ document.getElementById('submit_approve_access').click(); } } window.setInterval(consent, 100); Event handler SubmitListener.beforeSubmit: Note: There might be some redundant iteration of code in there, feel free to rewrite, main thing is: it works. I also expected that I could use the "Target" column to filter on the requests steps that start with "gmail*" but that didn't do it. So I fixed that in another way, together with providing some smart checking whether a new token generation is needed or not (gmail token is valid for 60 minutes). // Import the required classes import com.eviware.soapui.impl.rest.actions.oauth.OltuOAuth2ClientFacade; import com.eviware.soapui.support.editor.inspectors.auth.TokenType; import com.eviware.soapui.model.support.ModelSupport; import java.time.LocalDateTime import java.time.format.DateTimeFormatter def testStepName = context.getModelItem().getName() def expiresOn = context.expand('${#Project#expiresOn}') if (testStepName.toLowerCase().contains("gmail")) { // IF expiresOn == "" OR dateNow is > expiresOn then we need to get a new token. Otherwise the old should still do.... TimeZone.setDefault(TimeZone.getTimeZone('UTC')) TimeZone tz = TimeZone.getTimeZone("UTC") LocalDateTime dateNow = LocalDateTime.now() def patternUTC = "yyyy-MM-dd\'T\'HH:mm:ss.SSS\'Z\'" DateTimeFormatter dateFormatUTC = DateTimeFormatter.ofPattern(patternUTC).withLocale(Locale.US) String nowUtcFormat = dateNow.format(dateFormatUTC) LocalDateTime dateExpiresOn = dateNow.plusMinutes(55) String expiresOnUtcFormat = dateExpiresOn.format(dateFormatUTC) if (expiresOn == "") { /* log.info("Expires on is empty, so we need to run submitListener and get new token. We also write the new expiresOn to the project properties!") log.info("nowUtcFormat = " + nowUtcFormat) log.info("dateExpiresOn = " + expiresOnUtcFormat) log.info "let's run the submitListener.beforeSubmit to get a new accessToken. We know this one will be valid for 60 minutes, so we set a the expiresOn project property to now + 55 minutes" */ // Set up variables def project = ModelSupport.getModelItemProject(context.getModelItem()) project.setPropertyValue("expiresOn", expiresOnUtcFormat) def authProfile = project.getAuthRepository().getEntry("google") def oldToken = authProfile.getAccessToken() def tokenType = TokenType.ACCESS // Create a facade object def oAuthFacade = new OltuOAuth2ClientFacade(tokenType) // Request an access token in headless mode oAuthFacade.requestAccessToken(authProfile, true, true) // Wait until the access token gets updated int iteration = 0 while (oldToken == authProfile.getAccessToken() && iteration < 10) { sleep(500) iteration++ } // Post the info to the log log.info("Gmail authentication event handler: Project property \"expiresOn\" is empty! We get/set new token: " + authProfile.getAccessToken() + " with new expiresOn = " + expiresOnUtcFormat + ". The old token (with unknown expiresOn) was = " + oldToken) } else { def potentialNewExpiresOn = expiresOnUtcFormat //expiresOn retrieved from project properties dateExpiresOn = LocalDateTime.parse(expiresOn, dateFormatUTC) expiresOnUtcFormat = dateExpiresOn.format(dateFormatUTC) //log.info("dateExpiresOn = " + expiresOnUtcFormat) if (dateNow > dateExpiresOn) { // log.info "Expired! Let's get a new token..." // Set up variables def project = ModelSupport.getModelItemProject(context.getModelItem()) def authProfile = project.getAuthRepository().getEntry("google") def oldToken = authProfile.getAccessToken() def tokenType = TokenType.ACCESS // Create a facade object def oAuthFacade = new OltuOAuth2ClientFacade(tokenType) // Request an access token in headless mode oAuthFacade.requestAccessToken(authProfile, true, true) // Wait until the access token gets updated int iteration = 0 while (oldToken == authProfile.getAccessToken() && iteration < 10) { sleep(500) iteration++ } // Post the info to the log project.setPropertyValue("expiresOn", potentialNewExpiresOn) log.info("Token was expired! We get/set new token: " + authProfile.getAccessToken() + " with new expiresOn = " + potentialNewExpiresOn + ". The old token (expiresOn = $expiresOn vs now " + nowUtcFormat + ") was = " + oldToken) } else { //log.info "Not yet expired. Let's keep using the same old token (expiresOn = $expiresOn vs now " + nowUtcFormat + ")" } } } Setup script to delete all emails in the gmail inbox for proper start situation: I have a disabled test suite "WorkItem" with a test case named "GmailStartSituationCleanup" This test case has 4 steps: 1°GET gmail messagesList 2° Script "IterateOverAllGmailMessageIds" 3°DELETE gmail messageId 4°GET gmail messagesList-EmptyListCheck def testSuiteWorkItem = testRunner.testCase.testSuite.project.getTestSuiteByName("WorkItem") def testCaseGmailStartSituationCleanup = testSuiteWorkItem.getTestCaseByName("GmailStartSituationCleanup") testCaseGmailStartSituationCleanup.run(new com.eviware.soapui.support.types.StringToObjectMap(), false) The groovy test step 2°IterateOverAllGmailMessageIds = import com.eviware.soapui.support.JsonUtil def testStepAllMessages = testRunner.testCase.getTestStepAt(context.getCurrentStepIndex()-1) def teststepNameAllMessages = testStepAllMessages.getName() def testStepDeleteMessage = testRunner.testCase.getTestStepAt(context.getCurrentStepIndex()+1) testStepDeleteMessage.setDisabled(true) def testStepVerifAllDeleted = testRunner.testCase.getTestStepAt(context.getCurrentStepIndex()+2) testStepVerifAllDeleted.setDisabled(false) def responseStatus = testStepAllMessages.testRequest.response.responseHeaders["#status#"][0] if (responseStatus.contains("HTTP/1.1 2")){ def responseMessages = context.expand( '${'+teststepNameAllMessages+'#Response#$[\'messages\']}' ) if (responseMessages!= "" && responseMessages!= null){ def numberOfMessages = (JsonUtil.parseTrimmedText(responseMessages)).size() def id for (i=0;i<numberOfMessages;i++){ id = context.expand( '${'+teststepNameAllMessages+'#Response#$[\'messages\']['+i+'][\'id\']}' ) testStepDeleteMessage.setPropertyValue("messageId", id) log.info "Cleanup of gmail messages : Message "+(i+1).toString()+"/"+numberOfMessages.toString()+" with messageId $id will be deleted so we can continue with a proper starting situation..." testStepDeleteMessage.run(testRunner, context) } }else{ log.info "Cleanup of gmail messages : No messages found. We can continue with proper starting situation" testStepVerifAllDeleted.setDisabled(true) } }836Views3likes0CommentsGroovy Automated DataSource Loopers
I understand that this is a SoapUI Pro forum but I think creating the loop steps through groovy manually can be a good lesson and can help you customise your loops to your heart (and/or requirement)'s content! So without further ado this is how I script my groovy loopers NOTE: in this example I am connecting to an OracleDB to fuel my requests. 1) Structure: The structure within my test case is quite simple:loopStarterto connect to the db/initialise the data - storing the current information into properties,responseto send these properties off to the API through a request,loopEnderto either propagate or exit the loop if the condition is met. 2) loopStarter Connection to/creation of your data source, be it a database, excel etc In this example I will be using the groovy.sqlclass (documentation) to connect to my db, and store the results into properties import groovy.sql.Sql sql = Sql.newInstance(<connection details>) def res = sql.rows(<sql query>) def loopProperties = testRunner.testCase.getTestStepByName("loopProperties") //will initialise count in the loopProperties step if count does not yet exist if(!loopProperties.hasProperty("count")){ loopProperties.setPropertyValue("count","0") } def count = Integer.parseInt(loopProperties.getPropertyValue("count")) //store the properties from the current result loopProperties.setPropertyValue("x",res[count].x) loopProperties.setPropertyValue("y",res[count].y) loopProperties.setPropertyValue("querySize",(String)res.size()) sql.close() sql.rows returns an array of ArrayLists, so to access the current result (more on this later) we will use res[count]. Just to reiterate this step ONLY sets the properties to be sent off in the request.querySize is set so we can continue looping over all the results from the query. 3) response To then call your properties into your request step we will do this: <soapenv:Envelope namespace:ns="namespace"> <soapenv:Header/> <soapenv:Body> <ns:x>${loopProperties#x}</ns:x> <ns:y>${loopProperties#y}</ns:y> </soapenv:Body> </soapenv:Envelope> if you choose not to store your properties in a property step and wanted to store them at the test case level you can fetch them like so: ... <ns:x>${#TestCase#x}</ns:x> ... 4) loopEnder This is the loop exit condition. It will tell the loop whether or not there are more results from your query to send through the request. This is also a pretty simple step: - check count vs querySize - increment count if true - go to loopStarter if true, to update the properties (count has been increased andbecause of this, so will the current result) def loopProperties = testRunner.testCase.getTestStepByName("loopProperties") def count = Integer.parseInt(loopProperties.getPropertyValue("count")) def querySize = Integer.parseInt(loopProperties.getPropertyValue("querySize")) if( count<querySize-1 ){ count = count+1 loopProperties.setPropertyValue("count", (String)count) testRunner.gotoStepByName("loopStarter") } 5) reset count Unnecessary step but I like getting into the habbit of clearing all properties after I'm done, and this groovy script will do it for you def loopProperties = testRunner.testCase.getTestStepByName("loopProperties") String[] removals = loopProperties.getPropertyNames() for(i=0;i<removals.size();i++){ loopProperties.removeProperty(removals[i]) } I've been thinking of doing something similar to this for the Excel DataSource scriptOlga_Tmentioned, well similar to the loopStarter step anyway - doing it completely through groovy so SoapUI free users can do it too, I just need to look into parsing exel files with groovy. Anyways I hope this helps you all, Mo3KViews3likes4CommentsAdd an event for Report post-processing
The need In ReadyAPI there are nice options of reporting, however there is missing any function to do report post-processing. The post-processing needs are: to send a report via email put the report into a storage create a Jira item etc. Currently the report files are just placed in the output directory. On TestCase and TestSuite level there are Report script holders, but the purpose of the script is toextend the reporting options(it is executed before the reports are finished, not after), i.e. for report pre-processing. On the project level there is no such option. Solution proposal A good place to execute specific actions arethe events. But there is no "ReportListener.afterReport" event. Please add a new event ReportListener.afterReport which will be triggered after any report is finished by testrunner. Such event will be used by Groovy scripts to do the report post-processing.699Views2likes0CommentsProperties in SoapUI - case study
The possibility to use properties is a very important aspect in SoapUI automation. And although they are widely used they tend to be not very well understood. Indeed the name 'property' seems a little unfit for an element that (by all means) is presented by SmartBear as a variable to hold certain values for later use in the test execution cycle. For instance, one can have a 'sessionId' element to hold the current session string or one can have a 'cookie' element to hold some cookie value and so on. 1.Defining properties Properties can be defined at the project level, at the test suite level, at the test case level or in a special properties step. For instance, in order to define or edit a property at the test case level one must click on the desired test case in the left hand navigator and then just select the 'Custom Properties' tab. This is similar for any test suite or project. As said before there is also the possibility to use a special'properties step'. Thisstepmust be created inside a certain test case thus its properties will only be available inside that particular test case. 2. Reading properties 2.1 Referencing in SoapUI steps When wanting to reference a property, attention has to be paid to the level at which it was defined. There are different syntaxes that need to be written depending on it as presented in the table below: Level at which it is defined Syntax Example property name Example request Project level ${#Project#Property} Port http://myserver:${#Project#Port} Test suite level ${#TestSuite#Property} Port http://myserver:${#TestSuite#Port} Test case level ${#TestCase#Property} Port http://myserver:${#TestCase#Port} Special properties step ${#Property} Port http://myserver:${#Port} Following the above example, if one would have a project level property named Port with the value 8080, then that property could be used anywhere in the project by simply referencing it like${#Project#Port}. Example: When sending the request, the value of the ${#Project#Port} would be replaced by its value (in our example 8080). 2.2 Referencing in scripts In order to read a property in a script the following syntax is required: /** * Notes: * There are 4 properties defined at 4 levels: project, test suite, test case and special properties step level * The special properties step is simply called 'Properties' */ def stepProperty = testRunner.testCase.getTestStepByName( "Properties" ).getPropertyValue( "myCustomProperty" ) def testCaseProperty = testRunner.testCase.getPropertyValue( "myCustomProperty" ) def testSuiteProperty = testRunner.testCase.testSuite.getPropertyValue( "myCustomProperty" ) def projectProperty = testRunner.testCase.testSuite.project.getPropertyValue( "myCustomProperty" ) 3. Updating properties Updating tests can be done using a groovy script or a special transfer property step. Both of the methods are simple and straightforward but for the purpuse of this article only a groovy script example will be shown: /** * Notes: * This script changes 4 properties defined at 4 levels: project, test suite, test case and special properties step level * The special properties step is simply called 'Properties' */ testRunner.testCase.getTestStepByName( "Properties" ).setPropertyValue( "myCustomProperty", "newSpecialValue" ) testRunner.testCase.setPropertyValue( "myCustomProperty", "newTestCaseValue" ) testRunner.testCase.testSuite.setPropertyValue( "myCustomProperty", "newTestSuiteValue" ) testRunner.testCase.testSuite.project.setPropertyValue( "myCustomProperty", "newProjectValue" )460Views2likes0CommentsTip : Use of tags in command line via bamboo build - Run all tests while sending -T tags parameters
In our CI build pipeline (bamboo) I setup two parameters tagTestSuite and tagTestCase. This way, all our automation testers can specify, if needed, which test cases need to be ran based on certain tags. This works fine if there are indeed such tags passed on. But what if a project just wants to run all of its tests? These -T parameters cannot be left empty in the command line as that fails the build... Deleting them from the bamboo variables will then lose the standardized approach for all testers to do a tag-based execution. Seeing the documentation here (https://support.smartbear.com/readyapi/docs/functional/running/automating/cli.html)I noticed you can provide logical operators. So I thought I came up with a nice solution: I provide as default value for those parameters in bamboo a tag value that nobody would use and by setting the "!" operator in front of it I'd expected thatALL test suites/cases would run. So I've set parameters and resulted in a command line that has these : "-TTestSuite !itShouldRunAllTestSuites" "-TTestCase !itShouldRunAllTestCase". Since none of the testSuite/testCases has a tag "itShouldRunAllTestSuites" or "itShouldRunAllTestCases" I thought my project would be run in full.... But nope: error is shown:[errorlog] java.lang.Exception: The tag "itShouldRunAllTestSuites" was not found. Yes, I could go and add a tag to every single test suite and test case to work around this, but that could be tedious and not so future proof... Solution: ReadyAPI seems to look first for a tag and then complain it wasn't found. So I tried this: I've added the tags "itShouldRunAllTestCases" and "itShouldRunAllTestSuites" to my project but did not tag any of my testsuites/testcases with this tag. Executed above again and now ALL of my tests get's executed nicely!838Views2likes2CommentsAdd password parameter for the loadtestrunner
Hello, I'm using the latest version from RedayAPI (3.0.0) and my project is password protected - the properties are encrypted. When I want to run functional tests in pipeline, I can give the password over the command-line parameter -p but this parameter doesn't exists for the loadtestrunner. This fact is not an error but a missing function. Without this feature, encoding the properties in a project is a major drawback when you want to automate the load test because I must deactivate the passwort protection to run the loadtestrunner. This is a strong security restriction, especially if you want to protect the passwords/token/secrets used in cloud repositories. A lot of services are provided in the cload nowadays and the trend is increasing, besides the reason I ask the reader to vote for the function to introduce the password parameter for the loadtestrunner as well. Thanks in advance for the votes. Greetings, Giovanni1.8KViews2likes2CommentsAutomation script in ReadyAPI for Authentication
Hi, I am new to Ready API. I am trying to authenticate the user credentials and set it up as the script inside Auth Manager so that we can run the ready API test suites from the pipeline automatically. Manual set up for authentication is working fine and we are able to call the APIs and get results. We use Oauth2.0 authentication. The automation script that I have is not working. I am not sure what I am missing. This is what I tried referring to this document: https://support.smartbear.com/readyapi/docs/requests/auth/types/oauth2/automate/index.html var loginForm = document.querySelectorAll(".ab.cd"); var username = document.getElementById("username").value; var password = document.getElementById("password").value; var loginBtnForm = document.querySelector(".c2"); var loginBtn = document.querySelectorAll(".c3.c4.c5.c6.c7"); document.getElementById("username").value = "testusername"; document.getElementById("password").value = "testpassword"; loginBtn.click();140Views1like6CommentsTestEngine external JAR files - error - unable to resolve class
Background - We have test engine hosted on some linux server and we execute tests suing CURL cmd or POSTMAN. Given some external JARs When we add external JARs in /bin/ext folder of test engine server and re-start the test engine And run the tests using curl command from locally or using POSTMAN Then it throws error for groovy script test step saying unable to resolve class <className present in external JARs> on import statement itself. We have also configured .vmoptions of test engine server to read ext JARs but still it throws error. Locally, In ReadyAPI, we are placing those JARs in /bin/ext/ folder and it works perfectly fine. Any solution to this problem?200Views1like2Comments