How to get cookies in Groovy scripts?
Hi Community, Let meshare an answer to the above question with you 🙂 If theSession optionis enabled, ReadyAPI maintains the HTTP session on the TestCase level automatically, which means that it storescookiesreceived from the server and sends them back in the subsequent requests to the same endpoint. If you need to get programmatic access to the stored cookies, you can use this Groovy script (it's applicable to ReadyAPI v.1.7.0 and later): import org.apache.http.protocol.HttpContext import com.eviware.soapui.model.iface.SubmitContext import org.apache.http.impl.client.BasicCookieStore import org.apache.http.client.protocol.HttpClientContext HttpContext httpContext = context.getProperty(SubmitContext.HTTP_STATE_PROPERTY) BasicCookieStore cookieStore = httpContext.getAttribute(HttpClientContext.COOKIE_STORE) //iterate through the cookies store and output the name-value pairs to the log cookieStore.getCookies().each{ log.info(it.name + "=" + it.value) } Example:3.6KViews7likes0CommentsGmail 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.2KViews5likes2CommentsGroovy 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, Mo2.9KViews3likes4CommentsRamping up your usage of SOAP UI NG ? Check out our Free weekly Training webinar!
Hello all, Have you recently bought new Ready! API licenses and are about to kick start your API tests? Register here for our Free Weekly interactive Webinar, Next one is on the 7th of September( day after Labor day) What to expect? Advice on getting up and running on your SOAP and REST projects Hidden gems within the tool that you may not uncover on your own Personalized question-and-answer with our API experts If you have 10+ users and would like to get your users on-boarded, please reply to this message and I'll be in touch. Cheers, Katleen B Snr. Customer Success Advisor EMEA kbb(at)smartbear.com1.4KViews2likes0CommentsString.replaceAll not working in Groovy script
I am using a file as a template in order to create new files. The template file contains "placeholders" in the form of[plc]placeholderName[plc]. I want to replace the placeholder at runtime with values as shown in the groovy script. However the String.replaceAll() method seems not to work (placeholders don't get replaced) Am I missing something? The contents of the Template.xml are: My groovy script is: ThanksSolved403Views1like16CommentsGroovy - Save off hashmapped split() values to a Property
Hey, I'm back working with ReadyAPI! and I've forgotten loads in the last 2 years....I'm going through my groovy notes and I'm struggling. I have a full URL with 2 query parms - token and value and I'm using the split method to write out the values to the logging window. - the script is below: //code originally provided by nmrao as an answer to another question I posted def str = 'https://whatevs.azurewebsites.net/path?token=eyJ0eXAiOiJKV1Qi&value=123456789' def map = new URL(str).query?.split('&').collectEntries{ [(it.split('=').first()): it.split('=').last()]} log.info map.token log.info map.value log.info map so the logging window writes Sat Dec 16 21:46:36 GMT 2023: INFO: eyJ0eXAiOiJKV1Qi Sat Dec 16 21:46:36 GMT 2023: INFO: 123456789 Sat Dec 16 21:46:36 GMT 2023: INFO: {token=eyJ0eXAiOiJKV1Qi, value=123456789} However - I want to use the query parm values - I don't want to just log them 'token' and 'value' aren't actually properties/variables - so I can't write the associated property values out to a Properties step - cos those token and value values are just part of the value that makes up the whole string of '{token=eyJ0eXAiOiJKV1Qi, value=123456789}' I was thinking about passing the whole 'map' variable to the Properties step and try using some substring method like split() or something else - but I have a couple of problems: firstly - the map variable is a hashmap right? It's not like a normal property that I can pass to a properties step and then use groovyscript to manipulate - when I tried the following def str = 'https://whatevs.azurewebsites.net/path?token=eyJ0eXAiOiJKV1Qi&value=123456789' def map = new URL(str).query?.split('&').collectEntries{ [(it.split('=').first()): it.split('=').last()]} log.info map.token log.info map.value log.info map def propertiesStep = context.testCase.testSteps["Properties"] propertiesStep.setPropertyValue("map", map) I got an error warning of a problem - also - I noticed that in the logging - log.info map returns a whole string within curly braces - unlike when property values are normally written to the logging. Ok - that's it - could anyone advise how I can extract the two query parm values please? If your answers could add a bit of explaining (or just good commenting maybe?) so I can then read up - I'm reading up on hashmaps and the split() method at the moment - cos I don't really understand what the 'def map' line in the script is actually doing - I can see the result - I just don't understand how it;s getting there. Anyway -that's it - as always - I appreciate all and any help that anyone is kind enough to provide! Cheers, richSolved66Views1like6CommentsHow to build SQL query using strings whose value should be retrieved from TestCase property
Suppose i have the following properties defined in the Test Case Properties FromTime =2023-11-15 23:00:00 ToTime =2023-11-16 23:00:00 I want to replace the time given in the below query with the above test case property variables. How to do that in ReadyAPI JDBC datasource ? SELECT * FROM Table1 where TagId = 4314 and Time > '2023-11-15 23:00:00' and Time < '2023-11-16 23:00:00' I am able to retrieve integer values like TagId and use it by adding 'Prepared Properties' within the JDBC datasource window. But if i add the string and use it inside the query, data is not fetched as the string is not interpreted properly.362Views1like7CommentsTestEngine 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?184Views1like2CommentsHow to retrieve oauth2.0 access token for grant type Client Credentials in Groovy
I am using ReadyAPI (version 3.48) and able to manually retrieve the OAuth2.0 Access Token for grant type Client Credentials from this GUI. How can I make thisAccess Tokenretrieval automated by Groovy script so that I could use that token and assign it to a property variable and use in a subsequentREST Request I tried this Groovy script but getting error: (java.lang.NoClassDefFoundError: org/apache/ivy/util/MessageLogger) @Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.7.1') import groovyx.net.http.HTTPBuilder import groovyx.net.http.Method import groovyx.net.http.ContentType def clientId = 'df34a176-4d83-4ebf-82fd-ac6efba39e6a' def clientSecret = 'Yf.vZQuX__l4r2x75E_iqfJl_icH6Nr6.7' def tokenUrl = 'https://login.microsoftonline.com/75056d76-b628-4488-82b0-80b08b52d854/oauth2/v2.0/token' // First, we obtain an access token using the client credentials grant def http = new HTTPBuilder(tokenUrl) http.request(Method.POST, ContentType.URLENC) { req -> body = [ grant_type: 'client_credentials', client_id: clientId, client_secret: clientSecret ] response.success = { resp, reader -> // Extract the access token from the response def accessToken = resp.data.access_token // Now we can use the access token to make API requests makeApiRequest(accessToken) } } // Finally, we can use the access token to make API requests def makeApiRequest(accessToken) { def http = new HTTPBuilder('https://inest-npd.azcpggpc.ca') http.request(Method.GET, ContentType.JSON) { req -> headers['Authorization'] = "Bearer $accessToken" response.success = { resp, reader -> // Handle the API response println resp.data } } }382Views1like1Comment