Forum Discussion

AlbertSoapUI's avatar
AlbertSoapUI
Contributor
7 years ago

Configurable option for generating body content in Post requests

Hi,   Our team works on testing a REST API. It is set up by use of a Yaml file (Swagger). We use certain Post requests (verbs) in a lot of testcases. Sometimes the definitions of SUT changes, and...
  • AlbertSoapUI's avatar
    7 years ago

    Hi,

    What I did:

    I have a Properties block that contains a json Keynames as key in the block, and json paths as values. E.g. 

    key 'someKey', value 'a.b.c'

    This Properties block is placed in a testcase named 'templates'

    This properties block is used to store structure information about the body and has the type of request in it's name, e.g. PathsForSomeMessage.

    The testcase also contains a template Properties block that contains the same keys as in the other properties block. This is to be used for filling in specific values. The name also contains a reference to the type of request to be used, say ValuesForSomeMessage. E.g.

    key 'someKey', value empty

    In each testcase where you want to use the type of request you clone the values Properties block and fill in the wanted values, say 'someValue'.

    You also create/clone a DataGen teststep with a property for creation of body of wanted message, e.g. 'create somemessag body'. In this you call an external groovy script with the name of the messagetype as input.

    That will look like

    return com.myproject.soapui.common.JsonBodyContentGenerator.getBodyContent(testRunner,context,log, 'SomeMessage')

    In the request of your testcase you fill in the body of the teststep a reference to the DataGen step, '${GenerateParameters#create somemessage body}'

    The body generated will look like this

    {"a":{"b":{"c":"someValue"}}}

    Now if the needed structure of the body content of the body changes in the body structure, say for instance that "c" has to be in node "d" instead of node "b", all you have to do is change the path for key "c" to "a.d.c" in Property block PathsForSomeMessagePathsForSomeMessage. This will change the structure for all messages of SomeMessage, leaving the used value intact. So one generated body will look like: {"a":{"d":{"c":"someValue"}}}

     

    The external Grooovy script I have written:

    package com.myproject.soapui.common;
    
    import groovy.json.*
    
    public class JsonBodyContentGenerator {
        static getBodyContent(def testRunner, def context, def log, String requestName) {
            String templateName = 'PropertiesPathsFor' + requestName
            String valuesName = 'PropertiesValuesFor' + requestName
    
            def pathProperties = testRunner.testCase.testSuite.getTestCaseByName("templates").getTestStepByName(templateName)
            def valueProperties  = testRunner.testCase.getTestStepByName(valuesName)
    
            def body = [:]
            for (prop in valueProperties.propertyNames) {
                def path = 'body.' + pathProperties.properties[prop].value
                def value = valueProperties.properties[prop].value
                pathFinder(body, path, value)
            }
            return JsonOutput.toJson(body)
        }
    
        static pathFinder(int i, Map structure, String[] paths, String value) {
            def splitBracket = paths[i+1].split('\\[')
            def nextPath = splitBracket[0]
    
            if (i == (paths.size() - 2)) {
                if (splitBracket.size() > 1) {
                    def arrayToSet = structure.get(nextPath)
                    if (arrayToSet != null) {
                        arrayToSet.add(value)
                    } else {
                        arrayToSet = [ value ]
                    }
                    structure.put(nextPath, arrayToSet)
                } else {
                    structure.put(nextPath, value)
                }
            }
            else {
                if (structure.containsKey(paths[i+1])) {
                    Map mapForKeys = structure.get(paths[i+1])
                    pathFinder(++i, mapForKeys, paths, value)
                } else {
                    def keyForMap = nextPath
                    if (splitBracket.size() > 1) {
                        def mapIntoArray = [:]
                        pathFinder(0,mapIntoArray, paths[(i+1)..(paths.size()-1)] as String[], value)
    
                        def arrayToSet = structure.get(nextPath)
                        if (arrayToSet != null) {
                            arrayToSet.add(mapIntoArray)
                        } else {
                            arrayToSet = [ mapIntoArray ]
                        }
                        structure.put(nextPath, arrayToSet)
                    } else {
                        Map mapForKeys = [:]
                        structure.put(keyForMap, mapForKeys)
                        pathFinder(++i, mapForKeys, paths, value)
                    }
                }
            }
        }
    
        static pathFinder(Map structure, String path, String value) {
            def pathSteps = path.split('\\.')
            pathFinder(0,structure,pathSteps,value)
        }
    }