├── .github └── workflows │ └── python-app.yml ├── .gitignore ├── Doc ├── SoapLibrary.html ├── SoapLibrary.xml ├── img2_SoapLibrary.png └── img_SoapUI.png ├── LICENSE.md ├── README.md ├── SoapLibrary ├── SoapLibrary.py ├── __init__.py ├── config.py └── version.py ├── Tests ├── Requests │ ├── New_Request.xml │ ├── New_Request_Calculator.xml │ ├── Request_Calculator.xml │ ├── Request_Calculator_500.xml │ ├── busca_servicos.xml │ ├── consultaCEP.xml │ ├── request.xml │ └── request_capital.xml └── keyword_tests.robot ├── dist ├── robotframework-soaplibrary-0.8.tar.gz ├── robotframework-soaplibrary-0.9.tar.gz ├── robotframework-soaplibrary-1.0.tar.gz ├── robotframework-soaplibrary-1.2.tar.gz └── robotframework-soaplibrary-1.3.tar.gz ├── examples ├── request │ ├── cep.xml │ └── ip.xml └── samples.robot ├── requirements.txt ├── robotframework_soaplibrary.egg-info ├── PKG-INFO ├── SOURCES.txt ├── dependency_links.txt ├── requires.txt └── top_level.txt └── setup.py /.github/workflows/python-app.yml: -------------------------------------------------------------------------------- 1 | name: SOAP-Library 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'test/**' 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | run-robot-tests: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Set up Python 3.10 17 | uses: actions/setup-python@v4 18 | env: 19 | ROBOT_TESTS_DIR: ${{ github.workspace }}/Tests 20 | ROBOT_REPORTS_DIR: ${{ github.workspace }}/reports 21 | with: 22 | python-version: '3.10' 23 | - name: Install Dependencies 24 | run: pip install -r requirements.txt 25 | - name: Run Robot File 26 | run: robot -d ./reports -e offline ./Tests/keyword_tests.robot 27 | - name: Upload test results 28 | uses: actions/upload-artifact@v1 29 | if: always() 30 | with: 31 | name: robot_reports 32 | path: reports 33 | 34 | generate-robot-report: 35 | if: always() 36 | needs: [run-robot-tests] 37 | runs-on: ubuntu-latest 38 | steps: 39 | - name: Download reports 40 | uses: actions/download-artifact@v1 41 | with: 42 | name: robot_reports 43 | - name: Send report to commit 44 | uses: joonvena/robotframework-reporter-action@v2.4 45 | with: 46 | gh_access_token: ${{ secrets.GITHUB_TOKEN }} 47 | report_path: /robot_reports -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | response_test.xml 7 | report.html 8 | output.xml 9 | log.html 10 | -------------------------------------------------------------------------------- /Doc/SoapLibrary.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1.3 4 | SoapLibrary is a library for testing SOAP-based web services. 5 | 6 | SoapLibrary is based on [https://python-zeep.readthedocs.io/en/master/|Zeep], a modern SOAP client for Python. 7 | 8 | This library is designed for those who want to work with webservice automation as if they were using SoapUI, 9 | make a request through an XML file, and receive the response in another XML file. 10 | 11 | = Example = 12 | 13 | | ***** Settings ***** 14 | | Library SoapLibrary 15 | | Library OperatingSystem 16 | | 17 | | ***** Test Cases ***** 18 | | Simple Example 19 | | Create Soap Client http://endpoint.com/example.asmx?wsdl 20 | | ${response} Call SOAP Method Method_name arg1 arg2 21 | | Log ${response} 22 | | 23 | | Example With XML 24 | | Create Soap Client http://endpoint.com/example.asmx?wsdl 25 | | ${response} Call SOAP Method With XML ${CURDIR}/request.xml 26 | | ${text} Get Data From XML By Tag ${response} tag_name 27 | | Log ${text} 28 | | Save XML To File ${response} ${CURDIR} response_test 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | name 38 | 39 | 40 | args 41 | 42 | 43 | status 44 | None 45 | 46 | 47 | If the webservice have simple SOAP operation/method with few arguments, you can call the method with the given 48 | `name` and `args`. 49 | 50 | The first argument of the keyword ``name`` is the operation name of the ``SOAP operation/method`` 51 | [https://www.soapui.org/soap-and-wsdl/operations-and-requests.html|More information here] 52 | 53 | By default, this keyword fails if a status code different from 200 is returned as response, 54 | this behavior can be modified using the argument status=anything. 55 | 56 | *Input Arguments:* 57 | | *Name* | *Description* | 58 | | name | Name of the SOAP operation/method | 59 | | args | List of request entries | 60 | | status | if set as anything, return the error as a string | 61 | 62 | Note, this keyword uses the most basic method of sending a request, through zeep that creates the xml based 63 | on the wsdl definition. So this keyword does not store the response object, and therefore it is not possible 64 | to use the keyword `Get Last Response Object` after this one. 65 | 66 | *Example:* 67 | | ${response}= | Call SOAP Method | operation_name | arg1 | arg2 | 68 | | ${response}= | Call SOAP Method | operation_name | arg1 | arg2 | status=anything | 69 | If the webservice have simple SOAP operation/method with few arguments, you can call the method with the given `name` and `args`. 70 | 71 | 72 | 73 | 74 | string_xml 75 | 76 | 77 | headers 78 | {'Content-Type': 'text/xml; charset=utf-8'} 79 | 80 | 81 | status 82 | None 83 | 84 | 85 | Send a string representation of XML as a request to the SOAP client. 86 | The SOAP method is inside the XML string. 87 | 88 | By default, this keyword fails if a status code different from 200 is returned as response, 89 | this behavior can be modified using the argument status=anything. 90 | 91 | *Input Arguments:* 92 | | *Name* | *Description* | 93 | | string_xml | string representation of XML | 94 | | headers | dictionary with request headers. Default ``{'Content-Type': 'text/xml; charset=utf-8'}`` | 95 | | status | optional string: anything | 96 | 97 | *Example:* 98 | | ${response}= | Call SOAP Method With String XML | "<sample><Id>1</Id></sample>" | 99 | | ${response}= | Call SOAP Method With String XML | "<sample><Id>error</Id></sample>" | status=anything | 100 | Send a string representation of XML as a request to the SOAP client. The SOAP method is inside the XML string. 101 | 102 | 103 | 104 | 105 | xml 106 | 107 | 108 | headers 109 | {'Content-Type': 'text/xml; charset=utf-8'} 110 | 111 | 112 | status 113 | None 114 | 115 | 116 | Send an XML file as a request to the SOAP client. The path to the Request XML file is required as argument, 117 | the SOAP method is inside the XML file. 118 | 119 | By default, this keyword fails if a status code different from 200 is returned as response, 120 | this behavior can be modified using the argument status=anything. 121 | 122 | *Input Arguments:* 123 | | *Name* | *Description* | 124 | | xml | file path to xml file | 125 | | headers | dictionary with request headers. Default ``{'Content-Type': 'text/xml; charset=utf-8'}`` | 126 | | status | optional string: anything | 127 | 128 | *Example:* 129 | | ${response}= | Call SOAP Method With XML | ${CURDIR}${/}Request.xml | 130 | | ${response}= | Call SOAP Method With XML | ${CURDIR}${/}Request_status_500.xml | status=anything | 131 | Send an XML file as a request to the SOAP client. The path to the Request XML file is required as argument, the SOAP method is inside the XML file. 132 | 133 | 134 | 135 | 136 | xml_etree 137 | 138 | 139 | Convert the webservice response into a dictionary. 140 | 141 | *Input Arguments:* 142 | | *Name* | *Description* | 143 | | xml_etree | etree object of the xml to convert to dictionary | 144 | 145 | *Example:* 146 | | ${response}= | Call SOAP Method With XML | ${CURDIR}${/}Request.xml | 147 | | ${dict_response}= | Convert XML Response to Dictionary | ${response} | 148 | Convert the webservice response into a dictionary. 149 | 150 | 151 | 152 | 153 | url 154 | 155 | 156 | ssl_verify 157 | True 158 | 159 | 160 | client_cert 161 | None 162 | 163 | 164 | auth 165 | None 166 | 167 | 168 | use_binding_address 169 | False 170 | 171 | 172 | Loads a WSDL from the given ``url`` and creates a Zeep client. 173 | List all Available operations/methods with INFO log level. 174 | 175 | By default, server TLS certificate is validated. You can disable this behavior by setting ``ssl_verify`` 176 | to ``False`` (not recommended!). If your host uses a self-signed certificate, you can also pass the path of the 177 | CA_BUNDLE to ``sll_verify``. Accepted are only X.509 ASCII files (file extension .pem, sometimes .crt). 178 | If you have two different files for root and intermediate certificate, you must combine them manually into one. 179 | 180 | If your host requires client certificate based authentication, you can pass the 181 | path to your client certificate to the ``client_cert`` argument. 182 | 183 | For HTTP Basic Authentication, you can pass the list with username and password 184 | to the ``auth`` parameter. 185 | 186 | If you want to use the binding address in the requests, you need to pass use_binding_address=True in 187 | the argument. 188 | 189 | *Example:* 190 | | Create SOAP Client | http://endpoint.com?wsdl | 191 | | Create SOAP Client | https://endpoint.com?wsdl | ssl_verify=True | 192 | | Create SOAP Client | https://endpoint.com?wsdl | use_binding_address=True | 193 | | Create SOAP Client | https://endpoint.com?wsdl | client_cert=${CURDIR}${/}mycert.pem | 194 | | ${auth} | Create List | username | password | 195 | | Create SOAP Client | https://endpoint.com?wsdl | auth=${auth} | 196 | Loads a WSDL from the given ``url`` and creates a Zeep client. List all Available operations/methods with INFO log level. 197 | 198 | 199 | 200 | 201 | response 202 | 203 | 204 | Decodes texts that are base64 encoded. 205 | 206 | Returns the decoded response. 207 | 208 | *Input Arguments:* 209 | | *Name* | *Description* | 210 | | response | Response of the webservice coded in base64 | 211 | 212 | *Example:* 213 | | ${response}= | Call SOAP Method With XML | ${CURDIR}${/}Request.xml | 214 | | ${response_decoded}= | Decode Base64 | ${response} | 215 | Decodes texts that are base64 encoded. 216 | 217 | 218 | 219 | 220 | xml_file_path 221 | 222 | 223 | new_values_dict 224 | 225 | 226 | edited_request_name 227 | 228 | 229 | repeated_tags 230 | All 231 | 232 | 233 | Changes a field on the given XML to a new given value, the values must be in a dictionary xml_filepath must be 234 | a "template" of the request to the webservice. new_values_dict must be a dictionary with the keys 235 | and values to change. request_name will be the name of the new XMl file generated with the changed request. 236 | 237 | If there is a tag that appears more than once, all occurrences will be replaced by the new value by default. 238 | If you want to change a specific tag, inform the occurrence number in the repeated_tags argument. 239 | 240 | Returns the file path of the new Request file. 241 | 242 | *Input Arguments:* 243 | | *Name* | *Description* | 244 | | xml_file_path | file path to xml file | 245 | | new_values_dict | dictionary with tags as keys and tag value as value | 246 | | edited_request_name | name of the new XMl file generated with the changed request | 247 | | repeated_tags | Occurrence number of the repeated tag to change value | 248 | 249 | *Example*: 250 | | ${dict}= | Create Dictionary | tag_name1=SomeText | tag_name2=OtherText | 251 | | ${xml_edited}= | Edit XML Request | request_filepath | ${dict} | New_Request | 252 | | ${xml_edited}= | Edit XML Request | request_filepath | ${dict} | New_Request | repeated_tags=0 | 253 | Changes a field on the given XML to a new given value, the values must be in a dictionary xml_filepath must be a "template" of the request to the webservice. new_values_dict must be a dictionary with the keys and values to change. request_name will be the name of the new XMl file generated with the changed request. 254 | 255 | 256 | 257 | 258 | xml 259 | 260 | 261 | tag 262 | 263 | 264 | index 265 | 1 266 | 267 | 268 | Gets data from XML using a given tag. 269 | If the tag returns zero or more than one result, it will show a warning. The xml argument must be an 270 | etree object, can be used with the return of the keyword `Call SOAP Method With XML`. 271 | 272 | Returns the string representation of the value. 273 | 274 | *Input Arguments:* 275 | | *Name* | *Description* | 276 | | xml | xml etree object | 277 | | tag | tag to get value from | 278 | | index | tag index if there are multiple tags with the same name, starting at 1. Default is set to 1 | 279 | 280 | *Examples:* 281 | | ${response}= | Call SOAP Method With XML | ${CURDIR}${/}Request.xml | 282 | | ${value}= | Get Data From XML By Tag | ${response} | SomeTag | 283 | | ${value}= | Get Data From XML By Tag | ${response} | SomeTag | index=9 | 284 | Gets data from XML using a given tag. If the tag returns zero or more than one result, it will show a warning. The xml argument must be an etree object, can be used with the return of the keyword `Call SOAP Method With XML`. 285 | 286 | 287 | 288 | 289 | Gets the response object from the last request made. With the object in a variable, you can use the 290 | dot operator to get all the attributes of the response. 291 | 292 | Response object attributes: 293 | | apparent_encoding | 294 | | close | 295 | | connection | 296 | | content | 297 | | cookies | 298 | | elapsed | 299 | | encoding | 300 | | headers | 301 | | history | 302 | | is_permanent_redirect | 303 | | is_redirect | 304 | | iter_content | 305 | | iter_lines | 306 | | json | 307 | | links | 308 | | next | 309 | | ok | 310 | | raise_for_status | 311 | | raw | 312 | | reason | 313 | | request | 314 | | status_code | 315 | | text | 316 | | url | 317 | 318 | Note, this keyword only works after the execution of `Call SOAP Method With XML` or 319 | `Call SOAP Method With String XML`. 320 | 321 | *Example:* 322 | | ${response}= | Call SOAP Method With XML | ${CURDIR}${/}Request.xml | 323 | | ${response_object}= | Get Last Response Object | 324 | | ${response_header}= | Set Variable | ${response_object.headers} | 325 | | ${response_status}= | Set Variable | ${response_object.status_code} | 326 | Gets the response object from the last request made. With the object in a variable, you can use the dot operator to get all the attributes of the response. 327 | 328 | 329 | 330 | 331 | etree_xml 332 | 333 | 334 | save_folder 335 | 336 | 337 | file_name 338 | 339 | 340 | Save the webservice response as an XML file. 341 | 342 | Returns the file path of the newly created xml file. 343 | 344 | *Input Arguments:* 345 | | *Name* | *Description* | 346 | | etree_xml | etree object of the xml | 347 | | save_folder | directory to save the new file | 348 | | file_name | name of the new xml file without .xml | 349 | 350 | *Example*: 351 | | ${response}= | Call SOAP Method With XML | ${CURDIR}${/}Request.xml | 352 | | ${response_file}= | Save XML To File | ${response} | ${CURDIR} | response_file_name | 353 | Save the webservice response as an XML file. 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | -------------------------------------------------------------------------------- /Doc/img2_SoapLibrary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarketSquare/Robot-Framework-SOAP-Library/4c0a37b4b1f789765dae3808f81ab40535f1c0d6/Doc/img2_SoapLibrary.png -------------------------------------------------------------------------------- /Doc/img_SoapUI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarketSquare/Robot-Framework-SOAP-Library/4c0a37b4b1f789765dae3808f81ab40535f1c0d6/Doc/img_SoapUI.png -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 altranpt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![PyPi downloads](https://img.shields.io/pypi/dm/robotframework-soaplibrary.svg)](https://pypi.org/project/robotframework-soaplibrary) 2 | [![Total downloads](https://static.pepy.tech/personalized-badge/robotframework-soaplibrary?period=total&units=international_system&left_color=lightgrey&right_color=yellow&left_text=Total)](https://pypi.org/project/robotframework-soaplibrary) 3 | [![Latest Version](https://img.shields.io/pypi/v/robotframework-soaplibrary.svg)](https://pypi.org/project/robotframework-soaplibrary) 4 | [![tests](https://github.com/MarketSquare/Robot-Framework-SOAP-Library/actions/workflows/python-app.yml/badge.svg?branch=master)](https://github.com/MarketSquare/Robot-Framework-SOAP-Library/actions/workflows/python-app.yml) 5 | 6 | 7 | # Robot-Framework-SOAP-Library 8 | SOAP Library for Robot Framework 9 | 10 | ## Compatibility 11 | - _Python 3.7 +_ 12 | - _Zeep 4.2.1 +_ 13 | 14 | ## Introduction 15 | The SoapLibrary was created for those who want to use the Robot Framework as if they were using SoapUI, just send the request XML and get the response XML. 16 | 17 | ![alt text](https://github.com/MarketSquare/Robot-Framework-SOAP-Library/blob/master/Doc/img_SoapUI.png) 18 | 19 | ![alt text](https://github.com/MarketSquare/Robot-Framework-SOAP-Library/blob/master/Doc/img2_SoapLibrary.png) 20 | 21 | ## Instalation 22 | For the first time installation: 23 | ```commandline 24 | pip install robotframework-soaplibrary 25 | ``` 26 | Or you can upgrade with: 27 | ```commandline 28 | pip install --upgrade robotframework-soaplibrary 29 | ``` 30 | 31 | ## Example 32 | 33 | ```RobotFramework 34 | *** Settings *** 35 | Library SoapLibrary 36 | Library OperatingSystem 37 | 38 | *** Test Cases *** 39 | Example 40 | Create Soap Client http://endpoint.com/example.asmx?wsdl 41 | ${response} Call SOAP Method With XML ${CURDIR}/request.xml 42 | ${text} Get Data From XML By Tag ${response} tag_name 43 | Log ${text} 44 | Save XML To File ${response} ${CURDIR} response_test 45 | ``` 46 | 47 | ## Example with certificate 48 | 49 | You can see [here](https://michaelhallik.github.io/blog/2022/04/10/Using-OpenSSL-to-provide-the-RF-SoapLibrary-with-a-TLS-client-certificate) an example of how to use OPENSSL to access a webservice with TLS certificate. (Thanks Michael Hallik) 50 | 51 | ## Keyword Documentation 52 | 53 | You can find the keywords documentation [here](https://raw.githack.com/MarketSquare/Robot-Framework-SOAP-Library/master/Doc/SoapLibrary.html) 54 | 55 | ## Authors 56 | Initial development was sponsored by [Capgemini Engineering](https://www.capgemini.com/about-us/who-we-are/our-brands/capgemini-engineering/) 57 | - **Samuel Cabral** 58 | - **Joao Gomes** 59 | 60 | ## License 61 | This project is licensed under the MIT License - see the [LICENSE.md](https://github.com/MarketSquare/Robot-Framework-SOAP-Library/blob/master/LICENSE.md) file for details. 62 | -------------------------------------------------------------------------------- /SoapLibrary/SoapLibrary.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging.config 3 | import warnings 4 | import base64 5 | from .config import DICT_CONFIG 6 | from requests import Session 7 | from requests.auth import HTTPBasicAuth 8 | from zeep import Client 9 | from zeep.transports import Transport 10 | from zeep.wsdl.utils import etree 11 | from robot.api import logger 12 | from robot.api.deco import keyword 13 | from urllib3.exceptions import InsecureRequestWarning 14 | from .version import VERSION 15 | 16 | logging.config.dictConfig(DICT_CONFIG) 17 | # hide unnecessary warnings 18 | warnings.simplefilter("ignore", InsecureRequestWarning) 19 | logging.getLogger("urllib3").setLevel(logging.WARNING) 20 | 21 | DEFAULT_HEADERS = {'Content-Type': 'text/xml; charset=utf-8'} 22 | 23 | 24 | class SoapLibrary: 25 | ROBOT_LIBRARY_SCOPE = 'TEST SUITE' 26 | ROBOT_LIBRARY_VERSION = VERSION 27 | 28 | def __init__(self): 29 | self.client = None 30 | self.url = None 31 | self.response_obj = None 32 | 33 | @keyword("Create SOAP Client") 34 | def create_soap_client(self, url, ssl_verify=True, client_cert=None, auth=None, use_binding_address=False): 35 | """ 36 | Loads a WSDL from the given ``url`` and creates a Zeep client. 37 | List all Available operations/methods with INFO log level. 38 | 39 | By default, server TLS certificate is validated. You can disable this behavior by setting ``ssl_verify`` 40 | to ``False`` (not recommended!). If your host uses a self-signed certificate, you can also pass the path of the 41 | CA_BUNDLE to ``sll_verify``. Accepted are only X.509 ASCII files (file extension .pem, sometimes .crt). 42 | If you have two different files for root and intermediate certificate, you must combine them manually into one. 43 | 44 | If your host requires client certificate based authentication, you can pass the 45 | path to your client certificate to the ``client_cert`` argument. 46 | 47 | For HTTP Basic Authentication, you can pass the list with username and password 48 | to the ``auth`` parameter. 49 | 50 | If you want to use the binding address in the requests, you need to pass use_binding_address=True in 51 | the argument. 52 | 53 | *Example:* 54 | | Create SOAP Client | http://endpoint.com?wsdl | 55 | | Create SOAP Client | https://endpoint.com?wsdl | ssl_verify=True | 56 | | Create SOAP Client | https://endpoint.com?wsdl | use_binding_address=True | 57 | | Create SOAP Client | https://endpoint.com?wsdl | client_cert=${CURDIR}${/}mycert.pem | 58 | | ${auth} | Create List | username | password | 59 | | Create SOAP Client | https://endpoint.com?wsdl | auth=${auth} | 60 | """ 61 | self.url = url 62 | session = Session() 63 | session.verify = ssl_verify 64 | session.cert = client_cert 65 | session.auth = HTTPBasicAuth(*auth) if auth else None 66 | self.client = Client(self.url, transport=Transport(session=session)) 67 | logger.info('Connected to: %s' % self.client.wsdl.location) 68 | info = self.client.service.__dict__ 69 | operations = info["_operations"] 70 | logger.info('Available operations: %s' % list(operations)) 71 | if use_binding_address: 72 | self.url = self.client.service._binding_options['address'] 73 | 74 | @keyword("Call SOAP Method With XML") 75 | def call_soap_method_xml(self, xml, headers=DEFAULT_HEADERS, status=None): 76 | """ 77 | Send an XML file as a request to the SOAP client. The path to the Request XML file is required as argument, 78 | the SOAP method is inside the XML file. 79 | 80 | By default, this keyword fails if a status code different from 200 is returned as response, 81 | this behavior can be modified using the argument status=anything. 82 | 83 | *Input Arguments:* 84 | | *Name* | *Description* | 85 | | xml | file path to xml file | 86 | | headers | dictionary with request headers. Default ``{'Content-Type': 'text/xml; charset=utf-8'}`` | 87 | | status | optional string: anything | 88 | 89 | *Example:* 90 | | ${response}= | Call SOAP Method With XML | ${CURDIR}${/}Request.xml | 91 | | ${response}= | Call SOAP Method With XML | ${CURDIR}${/}Request_status_500.xml | status=anything | 92 | """ 93 | raw_text_xml = self._convert_xml_to_raw_text(xml) 94 | xml_obj = etree.fromstring(raw_text_xml) 95 | response = self.client.transport.post_xml(address=self.url, envelope=xml_obj, headers=headers) 96 | self._save_response_object(response) 97 | etree_response = self._parse_from_unicode(response.text) 98 | self._check_and_print_response(response, etree_response, status) 99 | return etree_response 100 | 101 | @keyword("Get Data From XML By Tag") 102 | def get_data_from_xml_tag(self, xml, tag, index=1): 103 | """ 104 | Gets data from XML using a given tag. 105 | If the tag returns zero or more than one result, it will show a warning. The xml argument must be an 106 | etree object, can be used with the return of the keyword `Call SOAP Method With XML`. 107 | 108 | Returns the string representation of the value. 109 | 110 | *Input Arguments:* 111 | | *Name* | *Description* | 112 | | xml | xml etree object | 113 | | tag | tag to get value from | 114 | | index | tag index if there are multiple tags with the same name, starting at 1. Default is set to 1 | 115 | 116 | *Examples:* 117 | | ${response}= | Call SOAP Method With XML | ${CURDIR}${/}Request.xml | 118 | | ${value}= | Get Data From XML By Tag | ${response} | SomeTag | 119 | | ${value}= | Get Data From XML By Tag | ${response} | SomeTag | index=9 | 120 | """ 121 | new_index = index - 1 122 | xpath = self._parse_xpath(tag) 123 | data_list = xml.xpath(xpath) 124 | if isinstance(data_list, (float, int)): 125 | return int(data_list) 126 | if len(data_list) == 0: 127 | logger.warn('The search "%s" did not return any result! Please confirm the tag!' % xpath) 128 | elif len(data_list) > 1: 129 | logger.debug('The tag you entered found %s items, returning the text in the index ' 130 | 'number %s, if you want a different index inform the argument index=N' % (len(data_list), index)) 131 | return data_list[new_index].text 132 | 133 | @keyword("Edit XML Request") 134 | def edit_xml(self, xml_file_path, new_values_dict, edited_request_name, repeated_tags='All'): 135 | """ 136 | Changes a field on the given XML to a new given value, the values must be in a dictionary xml_filepath must be 137 | a "template" of the request to the webservice. new_values_dict must be a dictionary with the keys 138 | and values to change. request_name will be the name of the new XMl file generated with the changed request. 139 | 140 | If there is a tag that appears more than once, all occurrences will be replaced by the new value by default. 141 | If you want to change a specific tag, inform the occurrence number in the repeated_tags argument. 142 | 143 | Returns the file path of the new Request file. 144 | 145 | *Input Arguments:* 146 | | *Name* | *Description* | 147 | | xml_file_path | file path to xml file | 148 | | new_values_dict | dictionary with tags as keys and tag value as value | 149 | | edited_request_name | name of the new XMl file generated with the changed request | 150 | | repeated_tags | Occurrence number of the repeated tag to change value | 151 | 152 | *Example*: 153 | | ${dict}= | Create Dictionary | tag_name1=SomeText | tag_name2=OtherText | 154 | | ${xml_edited}= | Edit XML Request | request_filepath | ${dict} | New_Request | 155 | | ${xml_edited}= | Edit XML Request | request_filepath | ${dict} | New_Request | repeated_tags=0 | 156 | """ 157 | string_xml = self._convert_xml_to_raw_text(xml_file_path) 158 | xml = self._convert_string_to_xml(string_xml) 159 | if not isinstance(new_values_dict, dict): 160 | raise Exception("new_values_dict argument must be a dictionary") 161 | for key, value in new_values_dict.items(): 162 | if len(xml.xpath(self._replace_xpath_by_local_name(key))) == 0: 163 | logger.warn('Tag "%s" not found' % key) 164 | continue 165 | xml_xpath = self._replace_xpath_by_local_name(key) 166 | count = int(xml.xpath(("count(%s)" % xml_xpath))) 167 | logger.debug("Found %s tags with xpath %s" % (str(count), xml_xpath)) 168 | if repeated_tags == 'All' or count < 2: 169 | for i in range(count): 170 | xml.xpath(xml_xpath)[i].text = value 171 | else: 172 | xml.xpath(xml_xpath)[int(repeated_tags)].text = value 173 | # Create new file with replaced request 174 | new_file_path = self._save_to_file(os.path.dirname(xml_file_path), edited_request_name, etree.tostring(xml)) 175 | return new_file_path 176 | 177 | @keyword("Save XML To File") 178 | def save_xml_to_file(self, etree_xml, save_folder, file_name): 179 | """ 180 | Save the webservice response as an XML file. 181 | 182 | Returns the file path of the newly created xml file. 183 | 184 | *Input Arguments:* 185 | | *Name* | *Description* | 186 | | etree_xml | etree object of the xml | 187 | | save_folder | directory to save the new file | 188 | | file_name | name of the new xml file without .xml | 189 | 190 | *Example*: 191 | | ${response}= | Call SOAP Method With XML | ${CURDIR}${/}Request.xml | 192 | | ${response_file}= | Save XML To File | ${response} | ${CURDIR} | response_file_name | 193 | """ 194 | new_file_path = self._save_to_file(save_folder, file_name, etree.tostring(etree_xml, pretty_print=True)) 195 | return new_file_path 196 | 197 | @keyword("Convert XML Response to Dictionary") 198 | def convert_response_dict(self, xml_etree): 199 | """ 200 | Convert the webservice response into a dictionary. 201 | 202 | *Input Arguments:* 203 | | *Name* | *Description* | 204 | | xml_etree | etree object of the xml to convert to dictionary | 205 | 206 | *Example:* 207 | | ${response}= | Call SOAP Method With XML | ${CURDIR}${/}Request.xml | 208 | | ${dict_response}= | Convert XML Response to Dictionary | ${response} | 209 | """ 210 | # Thanks to Jamie Murphy for this code: https://gist.github.com/jacobian/795571 211 | result = {} 212 | for element in xml_etree.iterchildren(): 213 | # Remove namespace prefix 214 | key = element.tag.split('}')[1] if '}' in element.tag else element.tag 215 | # Process element as tree element if the inner XML contains non-whitespace content 216 | if element.text and element.text.strip(): 217 | value = element.text 218 | else: 219 | value = self.convert_response_dict(element) 220 | if key in result: 221 | if type(result[key]) is list: 222 | result[key].append(value) 223 | else: 224 | tempvalue = result[key].copy() 225 | result[key] = [tempvalue, value] 226 | else: 227 | result[key] = value 228 | return result 229 | 230 | @keyword("Call SOAP Method") 231 | def call_soap_method(self, name, *args, status=None): 232 | """ 233 | If the webservice have simple SOAP operation/method with few arguments, you can call the method with the given 234 | `name` and `args`. 235 | 236 | The first argument of the keyword ``name`` is the operation name of the ``SOAP operation/method`` 237 | [https://www.soapui.org/soap-and-wsdl/operations-and-requests.html|More information here] 238 | 239 | By default, this keyword fails if a status code different from 200 is returned as response, 240 | this behavior can be modified using the argument status=anything. 241 | 242 | *Input Arguments:* 243 | | *Name* | *Description* | 244 | | name | Name of the SOAP operation/method | 245 | | args | List of request entries | 246 | | status | if set as anything, return the error as a string | 247 | 248 | Note, this keyword uses the most basic method of sending a request, through zeep that creates the xml based 249 | on the wsdl definition. So this keyword does not store the response object, and therefore it is not possible 250 | to use the keyword `Get Last Response Object` after this one. 251 | 252 | *Example:* 253 | | ${response}= | Call SOAP Method | operation_name | arg1 | arg2 | 254 | | ${response}= | Call SOAP Method | operation_name | arg1 | arg2 | status=anything | 255 | """ 256 | method = getattr(self.client.service, name) 257 | if status is None: 258 | response = method(*args) 259 | else: 260 | try: 261 | response = method(*args) 262 | except Exception as e: 263 | response = str(e) 264 | return response 265 | 266 | @keyword("Decode Base64") 267 | def decode_base64(self, response): 268 | """ 269 | Decodes texts that are base64 encoded. 270 | 271 | Returns the decoded response. 272 | 273 | *Input Arguments:* 274 | | *Name* | *Description* | 275 | | response | Response of the webservice coded in base64 | 276 | 277 | *Example:* 278 | | ${response}= | Call SOAP Method With XML | ${CURDIR}${/}Request.xml | 279 | | ${response_decoded}= | Decode Base64 | ${response} | 280 | """ 281 | response_decode = base64.b64decode(response) 282 | return response_decode.decode('utf-8', 'ignore') 283 | 284 | @keyword("Call SOAP Method With String XML") 285 | def call_soap_method_string_xml(self, string_xml, headers=DEFAULT_HEADERS, status=None): 286 | """ 287 | Send a string representation of XML as a request to the SOAP client. 288 | The SOAP method is inside the XML string. 289 | 290 | By default, this keyword fails if a status code different from 200 is returned as response, 291 | this behavior can be modified using the argument status=anything. 292 | 293 | *Input Arguments:* 294 | | *Name* | *Description* | 295 | | string_xml | string representation of XML | 296 | | headers | dictionary with request headers. Default ``{'Content-Type': 'text/xml; charset=utf-8'}`` | 297 | | status | optional string: anything | 298 | 299 | *Example:* 300 | | ${response}= | Call SOAP Method With String XML | "1" | 301 | | ${response}= | Call SOAP Method With String XML | "error" | status=anything | 302 | """ 303 | xml_obj = etree.fromstring(string_xml) 304 | response = self.client.transport.post_xml(address=self.url, envelope=xml_obj, headers=headers) 305 | self._save_response_object(response) 306 | etree_response = self._parse_from_unicode(response.text) 307 | self._check_and_print_response(response, etree_response, status) 308 | return etree_response 309 | 310 | @keyword("Get Last Response Object") 311 | def get_last_response_object(self): 312 | """ 313 | Gets the response object from the last request made. With the object in a variable, you can use the 314 | dot operator to get all the attributes of the response. 315 | 316 | Response object attributes: 317 | | apparent_encoding | 318 | | close | 319 | | connection | 320 | | content | 321 | | cookies | 322 | | elapsed | 323 | | encoding | 324 | | headers | 325 | | history | 326 | | is_permanent_redirect | 327 | | is_redirect | 328 | | iter_content | 329 | | iter_lines | 330 | | json | 331 | | links | 332 | | next | 333 | | ok | 334 | | raise_for_status | 335 | | raw | 336 | | reason | 337 | | request | 338 | | status_code | 339 | | text | 340 | | url | 341 | 342 | Note, this keyword only works after the execution of `Call SOAP Method With XML` or 343 | `Call SOAP Method With String XML`. 344 | 345 | *Example:* 346 | | ${response}= | Call SOAP Method With XML | ${CURDIR}${/}Request.xml | 347 | | ${response_object}= | Get Last Response Object | 348 | | ${response_header}= | Set Variable | ${response_object.headers} | 349 | | ${response_status}= | Set Variable | ${response_object.status_code} | 350 | """ 351 | return self.response_obj 352 | 353 | @staticmethod 354 | def _convert_xml_to_raw_text(xml_file_path): 355 | """ 356 | Converts a xml file into a string. 357 | 358 | :param xml_file_path: xml file path. 359 | :return: string with xml content. 360 | """ 361 | file_content = open(xml_file_path, 'r') 362 | xml = '' 363 | for line in file_content: 364 | xml += line 365 | file_content.close() 366 | return xml 367 | 368 | @staticmethod 369 | def _parse_from_unicode(unicode_str): 370 | """ 371 | Parses a unicode string. 372 | 373 | :param unicode_str: unicode string. 374 | :return: parsed string. 375 | """ 376 | utf8_parser = etree.XMLParser(encoding='utf-8') 377 | string_utf8 = unicode_str.encode('utf-8') 378 | return etree.fromstring(string_utf8, parser=utf8_parser) 379 | 380 | def _parse_xpath(self, tags): 381 | """ 382 | Parses a single xpath or a list of xml tags. 383 | 384 | :param tags: string for a single xml tag or list for multiple xml tags. 385 | :return: parsed xpath. 386 | """ 387 | xpath = '' 388 | if isinstance(tags, list): 389 | for el in tags: 390 | xpath += self._replace_xpath_by_local_name(el) 391 | else: 392 | xpath += self._replace_xpath_by_local_name(tags) 393 | return xpath 394 | 395 | def _check_and_print_response(self, response, etree_response, status): 396 | """ 397 | Check if the response is 200 when the status is None and pretty print in the robot log. 398 | 399 | :param response: response object. 400 | :param etree_response: response object in etree format. 401 | :param status: if is not None, then don´t raise error. 402 | """ 403 | logger.debug('URL: %s' % response.url) 404 | logger.debug(etree.tostring(etree_response, pretty_print=True, encoding='unicode')) 405 | if status is None and response.status_code != 200: 406 | raise AssertionError('Request Error! Status Code: %s! Reason: %s' % (response.status_code, response.reason)) 407 | self._print_request_info(etree_response) 408 | 409 | def _save_response_object(self, response): 410 | """ 411 | Log the response status code in Robot Framework log and save the response object 412 | for use in the keyword 'Get Last Response Object' 413 | 414 | :param response, zeep response object. 415 | """ 416 | logger.info('Status code: %s' % response.status_code) 417 | self.response_obj = response 418 | 419 | @staticmethod 420 | def _convert_string_to_xml(xml_string): 421 | """ 422 | Converts a given string to xml object using etree. 423 | 424 | :param xml_string: string with xml content. 425 | :return: xml object. 426 | """ 427 | return etree.fromstring(xml_string) 428 | 429 | @staticmethod 430 | def _replace_xpath_by_local_name(xpath_tag): 431 | """ 432 | Replaces the given xpath_tag in a xpath using name() function. 433 | Returns the replaced xpath. 434 | 435 | :param xpath_tag: tag to replace with in xpath. 436 | :return: replaced xpath tag. 437 | """ 438 | local_name_xpath = "//*[name()='%s']" 439 | return local_name_xpath % xpath_tag 440 | 441 | @staticmethod 442 | def _save_to_file(save_folder, file_name, text): 443 | """ 444 | Saves text into a new file. 445 | 446 | :param save_folder: folder to save the new xml file. 447 | :param file_name: name of the new file. 448 | :param text: file text. 449 | :return new file path. 450 | """ 451 | new_file_name = "%s.xml" % file_name 452 | new_file_path = os.path.join(save_folder, new_file_name) 453 | request_file = open(new_file_path, 'wb') 454 | request_file.write(text) 455 | request_file.close() 456 | return new_file_path 457 | 458 | @staticmethod 459 | def _print_request_info(etree_response): 460 | """ 461 | Pretty print for robot framework log. 462 | """ 463 | log_header_response_from_ws = 'Response from webservice: ' 464 | logger.info(log_header_response_from_ws, html=True) 465 | logger.info(etree.tostring(etree_response, pretty_print=True, encoding='unicode')) 466 | -------------------------------------------------------------------------------- /SoapLibrary/__init__.py: -------------------------------------------------------------------------------- 1 | from .SoapLibrary import SoapLibrary 2 | 3 | 4 | class SoapLibrary(SoapLibrary): 5 | """ 6 | SoapLibrary is a library for testing SOAP-based web services. 7 | 8 | SoapLibrary is based on [https://python-zeep.readthedocs.io/en/master/|Zeep], a modern SOAP client for Python. 9 | 10 | This library is designed for those who want to work with webservice automation as if they were using SoapUI, 11 | make a request through an XML file, and receive the response in another XML file. 12 | 13 | = Example = 14 | 15 | | ***** Settings ***** 16 | | Library SoapLibrary 17 | | Library OperatingSystem 18 | | 19 | | ***** Test Cases ***** 20 | | Simple Example 21 | | Create Soap Client http://endpoint.com/example.asmx?wsdl 22 | | ${response} Call SOAP Method Method_name arg1 arg2 23 | | Log ${response} 24 | | 25 | | Example With XML 26 | | Create Soap Client http://endpoint.com/example.asmx?wsdl 27 | | ${response} Call SOAP Method With XML ${CURDIR}/request.xml 28 | | ${text} Get Data From XML By Tag ${response} tag_name 29 | | Log ${text} 30 | | Save XML To File ${response} ${CURDIR} response_test 31 | """ 32 | def __init__(self): 33 | pass 34 | -------------------------------------------------------------------------------- /SoapLibrary/config.py: -------------------------------------------------------------------------------- 1 | DICT_CONFIG = { 2 | 'version': 1, 3 | 'formatters': { 4 | 'verbose': { 5 | 'format': '%(name)s: %(message)s' 6 | } 7 | }, 8 | 'handlers': { 9 | 'console': { 10 | 'level': 'DEBUG', 11 | 'class': 'logging.StreamHandler', 12 | 'formatter': 'verbose', 13 | }, 14 | }, 15 | 'loggers': { 16 | 'zeep.transports': { 17 | 'level': 'DEBUG', 18 | 'propagate': True, 19 | 'handlers': ['console'], 20 | }, 21 | } 22 | } -------------------------------------------------------------------------------- /SoapLibrary/version.py: -------------------------------------------------------------------------------- 1 | VERSION = '1.3' 2 | -------------------------------------------------------------------------------- /Tests/Requests/New_Request.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | EmailAddress 4 | 5 | 07-06-2020 6 | AAAAA 7 | 8 | 9 | 0000 10 | 11 | 12 | 13 | PhoneNumber 14 | 15 | 07-06-2020 16 | BBBB 17 | 18 | 19 | 0000 20 | 21 | 22 | 23 | PhoneNumber 24 | 25 | 07-06-2020 26 | 27 | 28 | 0000 29 | 30 | 31 | -------------------------------------------------------------------------------- /Tests/Requests/New_Request_Calculator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 5 | 5 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tests/Requests/Request_Calculator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 3 5 | 5 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tests/Requests/Request_Calculator_500.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 3 5 | a 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tests/Requests/busca_servicos.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | sigep 6 | n5f9t8 7 | 8 | 9 | -------------------------------------------------------------------------------- /Tests/Requests/consultaCEP.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 88132010 6 | sigep 7 | n5f9t8 8 | 9 | 10 | -------------------------------------------------------------------------------- /Tests/Requests/request.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | EmailAddress 4 | 5 | 2019-06-03 6 | AAAAA 7 | 8 | 9 | 0000 10 | 11 | 12 | 13 | PhoneNumber 14 | 15 | 2019-06-03 16 | BBBB 17 | 18 | 19 | 0000 20 | 21 | 22 | 23 | PhoneNumber 24 | 25 | 2019-06-03 26 | 27 | 28 | 0000 29 | 30 | 31 | -------------------------------------------------------------------------------- /Tests/Requests/request_capital.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PT 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tests/keyword_tests.robot: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Library ../SoapLibrary/ 3 | Library Collections 4 | Library OperatingSystem 5 | Library XML use_lxml=True 6 | Library Process 7 | 8 | *** Variables *** 9 | ${requests_dir} ${CURDIR}${/}Requests 10 | ${wsdl_correios} https://apphom.correios.com.br/SigepMasterJPA/AtendeClienteService/AtendeCliente?wsdl 11 | ${wsdl_country_info} http://webservices.oorsprong.org/websamples.countryinfo/CountryInfoService.wso?wsdl 12 | ${wsdl_calculator} https://ecs.syr.edu/faculty/fawcett/Handouts/cse775/code/calcWebService/Calc.asmx?wsdl 13 | ${request_string} 53 14 | ${request_string_500} a3 15 | 16 | *** Test Cases *** 17 | Test Call Soap Method 18 | [Tags] calculator 19 | Create Soap Client ${wsdl_calculator} ssl_verify=False 20 | ${response} Call SOAP Method Add 2 1 21 | should be equal as integers 3 ${response} 22 | 23 | Test Call Soap Method Error 24 | [Tags] calculator 25 | Create Soap Client ${wsdl_calculator} ssl_verify=False 26 | ${response} Call SOAP Method Add 2 X status=anything 27 | Should Contain ${response} Input string was not in a correct format. 28 | 29 | Test Read 30 | [Tags] calculator 31 | Create Soap Client ${wsdl_calculator} ssl_verify=False 32 | ${response} Call SOAP Method With XML ${requests_dir}${/}Request_Calculator.xml 33 | ${result} Get Data From XML By Tag ${response} AddResult 34 | should be equal 8 ${result} 35 | 36 | Test Read With Binding Address 37 | [Tags] calculator 38 | Create Soap Client ${wsdl_calculator} ssl_verify=False use_binding_address=True 39 | ${response} Call SOAP Method With XML ${requests_dir}${/}Request_Calculator.xml 40 | ${result} Get Data From XML By Tag ${response} AddResult 41 | should be equal 8 ${result} 42 | 43 | Test Read String Xml 44 | [Tags] calculator 45 | Create Soap Client ${wsdl_calculator} ssl_verify=False 46 | ${response} Call SOAP Method With String XML ${request_string} 47 | ${result} Get Data From XML By Tag ${response} AddResult 48 | should be equal 8 ${result} 49 | 50 | Test Edit And Read 51 | [Tags] calculator 52 | Remove File ${requests_dir}${/}New_Request_Calculator.xml 53 | Create Soap Client ${wsdl_calculator} ssl_verify=False 54 | ${dict} Create Dictionary a=9 b=5 55 | ${xml_edited} Edit XML Request ${requests_dir}${/}Request_Calculator.xml ${dict} New_Request_Calculator 56 | ${response} Call SOAP Method With XML ${xml_edited} 57 | ${result} Get Data From XML By Tag ${response} AddResult 58 | should be equal 14 ${result} 59 | Should Exist ${requests_dir}${/}New_Request_Calculator.xml 60 | 61 | Test Call Soap Method With XML Anything 62 | [Tags] calculator 63 | Create Soap Client ${wsdl_calculator} ssl_verify=False 64 | ${response} Call SOAP Method With XML ${requests_dir}${/}Request_Calculator_500.xml status=anything 65 | ${result} Get Data From XML By Tag ${response} faultstring 66 | log ${result} 67 | Should Contain ${result} Server was unable to read request. 68 | 69 | Test Call SOAP Method With String XML Anything 70 | [Tags] calculator 71 | Create Soap Client ${wsdl_calculator} ssl_verify=False 72 | ${response} Call SOAP Method With String XML ${request_string_500} status=anything 73 | ${result} Get Data From XML By Tag ${response} faultstring 74 | log ${result} 75 | Should Contain ${result} Server was unable to read request. 76 | 77 | Test Read UTF8 78 | [Tags] country_info 79 | #todo find an API with response in utf8 80 | Create Soap Client ${wsdl_country_info} 81 | ${response} Call SOAP Method With XML ${requests_dir}${/}request_capital.xml 82 | ${City} Get Data From XML By Tag ${response} m:CapitalCityResult 83 | should be equal as strings ${City} Lisbon 84 | 85 | Test Get Last Response Object 86 | [Tags] country_info 87 | Create Soap Client ${wsdl_country_info} 88 | Call SOAP Method With XML ${requests_dir}${/}request_capital.xml 89 | ${response_object} Get Last Response Object 90 | Should Be Equal As Integers ${response_object.status_code} 200 91 | Should Contain ${response_object.text} Lisbon 92 | Dictionary Should Contain Key ${response_object.headers} Content-Type 93 | 94 | Test Save File Response 95 | [Tags] country_info 96 | Remove File ${CURDIR}${/}response_test.xml 97 | Create Soap Client ${wsdl_country_info} 98 | ${response} Call SOAP Method With XML ${requests_dir}${/}request_capital.xml 99 | ${file} Save XML To File ${response} ${CURDIR} response_test 100 | Should Exist ${CURDIR}${/}response_test.xml 101 | 102 | Test Read Tags With Index 103 | [Tags] correios 104 | Create Soap Client ${wsdl_correios} 105 | ${response} Call SOAP Method With XML ${requests_dir}${/}busca_servicos.xml 106 | ${codigo} Get Data From XML By Tag ${response} codigo index=13 107 | Should Be Equal As Integers ${codigo} 032 108 | 109 | Test Response To Dict 110 | [Tags] correios 111 | Create Soap Client ${wsdl_correios} 112 | ${response} Call SOAP Method With XML ${requests_dir}${/}consultaCEP.xml 113 | ${dict_response} Convert XML Response to Dictionary ${response} 114 | ${type} evaluate str(type(${dict_response})) 115 | Should Contain ${type} 'dict' 116 | ${body} Get From Dictionary ${dict_response} Body 117 | ${city} Set Variable ${dict_response}[Body][consultaCEPResponse][return][cidade] 118 | ${uf} Set Variable ${dict_response}[Body][consultaCEPResponse][return][uf] 119 | Should Be Equal ${city} Palhoça 120 | Should Be Equal ${uf} SC 121 | 122 | Test Edit XML Request 1 123 | [Tags] edit_xml 124 | [Documentation] Change all names, dates and reasons tags 125 | ${new_value_dict} Create Dictionary startDate=15-01-2020 name=Joaquim Reason=1515 126 | ${xml_edited} Edit XML Request ${requests_dir}${/}request.xml ${new_value_dict} New_Request repeated_tags=0 127 | ${new_value_dict} Create Dictionary startDate=16-01-2020 name2=Joao Reason=1616 128 | ${xml_edited} Edit XML Request ${xml_edited} ${new_value_dict} New_Request repeated_tags=1 129 | ${new_value_dict} Create Dictionary startDate=17-01-2020 Reason=1717 130 | ${xml_edited} Edit XML Request ${xml_edited} ${new_value_dict} New_Request repeated_tags=2 131 | ${data} Parse XML ${requests_dir}${/}New_Request.xml keep_clark_notation=True 132 | ${text_name} Evaluate Xpath ${data} //name 133 | Should be equal ${text_name[0].text} Joaquim 134 | ${text_name2} Evaluate Xpath ${data} //name2 135 | Should be equal ${text_name2[0].text} Joao 136 | ${text_date} Evaluate Xpath ${data} //startDate 137 | Should be equal ${text_date[0].text} 15-01-2020 138 | Should be equal ${text_date[1].text} 16-01-2020 139 | Should be equal ${text_date[2].text} 17-01-2020 140 | ${text_reason} Evaluate Xpath ${data} //Reason 141 | Should be equal ${text_reason[0].text} 1515 142 | Should be equal ${text_reason[1].text} 1616 143 | Should be equal ${text_reason[2].text} 1717 144 | 145 | Test Edit XML Request 2 146 | [Tags] edit_xml 147 | [Documentation] Change name, date and reason on tag 0 148 | ${new_value_dict} Create Dictionary startDate=20-01-2020 name=Maria Reason=2020 149 | ${xml_edited} Edit XML Request ${requests_dir}${/}request.xml ${new_value_dict} New_Request repeated_tags=0 150 | ${data} Parse XML ${requests_dir}${/}New_Request.xml keep_clark_notation=True 151 | ${text_name} Evaluate Xpath ${data} //name 152 | Should be equal ${text_name[0].text} Maria 153 | ${text_name2} Evaluate Xpath ${data} //name2 154 | Should be equal ${text_name2[0].text} BBBB 155 | ${text_date} Evaluate Xpath ${data} //startDate 156 | Should be equal ${text_date[0].text} 20-01-2020 157 | Should be equal ${text_date[1].text} 2019-06-03 158 | Should be equal ${text_date[2].text} 2019-06-03 159 | ${text_reason} Evaluate Xpath ${data} //Reason 160 | Should be equal ${text_reason[0].text} 2020 161 | Should be equal ${text_reason[1].text} 0000 162 | Should be equal ${text_reason[2].text} 0000 163 | 164 | Test Edit XML Request 3 165 | [Tags] edit_xml 166 | [Documentation] Change name2, date and reason on tag 1 167 | ${new_value_dict} Create Dictionary startDate=22-01-2020 name2=Joana Reason=2222 168 | ${xml_edited} Edit XML Request ${requests_dir}${/}request.xml ${new_value_dict} New_Request repeated_tags=1 169 | ${data} Parse XML ${requests_dir}${/}New_Request.xml keep_clark_notation=True 170 | ${text_name} Evaluate Xpath ${data} //name 171 | Should be equal ${text_name[0].text} AAAAA 172 | ${text_name2} Evaluate Xpath ${data} //name2 173 | Should be equal ${text_name2[0].text} Joana 174 | ${text_date} Evaluate Xpath ${data} //startDate 175 | Should be equal ${text_date[0].text} 2019-06-03 176 | Should be equal ${text_date[1].text} 22-01-2020 177 | Should be equal ${text_date[2].text} 2019-06-03 178 | ${text_reason} Evaluate Xpath ${data} //Reason 179 | Should be equal ${text_reason[0].text} 0000 180 | Should be equal ${text_reason[1].text} 2222 181 | Should be equal ${text_reason[2].text} 0000 182 | 183 | Test Edit XML Request 4 184 | [Tags] edit_xml 185 | [Documentation] Change date and Reason on tag 2 186 | ${new_value_dict} Create Dictionary startDate=25-01-2020 Reason=2525 187 | ${xml_edited} Edit XML Request ${requests_dir}${/}request.xml ${new_value_dict} New_Request repeated_tags=2 188 | ${data} Parse XML ${requests_dir}${/}New_Request.xml keep_clark_notation=True 189 | ${text_name} Evaluate Xpath ${data} //name 190 | Should be equal ${text_name[0].text} AAAAA 191 | ${text_name2} Evaluate Xpath ${data} //name2 192 | Should be equal ${text_name2[0].text} BBBB 193 | ${text_date} Evaluate Xpath ${data} //startDate 194 | Should be equal ${text_date[0].text} 2019-06-03 195 | Should be equal ${text_date[1].text} 2019-06-03 196 | Should be equal ${text_date[2].text} 25-01-2020 197 | ${text_reason} Evaluate Xpath ${data} //Reason 198 | Should be equal ${text_reason[0].text} 0000 199 | Should be equal ${text_reason[1].text} 0000 200 | Should be equal ${text_reason[2].text} 2525 201 | 202 | Test Edit XML Request 5 203 | [Tags] edit_xml 204 | [Documentation] Change name, date and reason in Tags 0 and 1 205 | ${new_value_dict} Create Dictionary startDate=15-01-2020 name=Joaquim Reason=1515 206 | ${xml_edited} Edit XML Request ${requests_dir}${/}request.xml ${new_value_dict} New_Request repeated_tags=0 207 | ${new_value_dict} Create Dictionary startDate=16-01-2020 name2=Joao Reason=1616 208 | ${xml_edited} Edit XML Request ${xml_edited} ${new_value_dict} New_Request repeated_tags=1 209 | ${data} Parse XML ${requests_dir}${/}New_Request.xml keep_clark_notation=True 210 | ${text_name} Evaluate Xpath ${data} //name 211 | Should be equal ${text_name[0].text} Joaquim 212 | ${text_name2} Evaluate Xpath ${data} //name2 213 | Should be equal ${text_name2[0].text} Joao 214 | ${text_date} Evaluate Xpath ${data} //startDate 215 | Should be equal ${text_date[0].text} 15-01-2020 216 | Should be equal ${text_date[1].text} 16-01-2020 217 | Should be equal ${text_date[2].text} 2019-06-03 218 | ${text_reason} Evaluate Xpath ${data} //Reason 219 | Should be equal ${text_reason[0].text} 1515 220 | Should be equal ${text_reason[1].text} 1616 221 | Should be equal ${text_reason[2].text} 0000 222 | 223 | Test Edit XML Request 6 224 | [Tags] edit_xml 225 | [Documentation] Change name, date and reason in Tags 1 and 2 226 | ${new_value_dict} Create Dictionary startDate=15-01-2020 name2=Joaquim Reason=1515 227 | ${xml_edited} Edit XML Request ${requests_dir}${/}request.xml ${new_value_dict} New_Request repeated_tags=1 228 | ${new_value_dict} Create Dictionary startDate=16-01-2020 Reason=1616 229 | ${xml_edited} Edit XML Request ${xml_edited} ${new_value_dict} New_Request repeated_tags=2 230 | ${data} Parse XML ${requests_dir}${/}New_Request.xml keep_clark_notation=True 231 | ${text_name} Evaluate Xpath ${data} //name 232 | Should be equal ${text_name[0].text} AAAAA 233 | ${text_name2} Evaluate Xpath ${data} //name2 234 | Should be equal ${text_name2[0].text} Joaquim 235 | ${text_date} Evaluate Xpath ${data} //startDate 236 | Should be equal ${text_date[0].text} 2019-06-03 237 | Should be equal ${text_date[1].text} 15-01-2020 238 | Should be equal ${text_date[2].text} 16-01-2020 239 | ${text_reason} Evaluate Xpath ${data} //Reason 240 | Should be equal ${text_reason[0].text} 0000 241 | Should be equal ${text_reason[1].text} 1515 242 | Should be equal ${text_reason[2].text} 1616 243 | 244 | Test Edit XML Request 7 245 | [Tags] edit_xml 246 | [Documentation] Change only the name tag 247 | ${new_value_dict} Create Dictionary name=Carlota 248 | ${xml_edited} Edit XML Request ${requests_dir}${/}request.xml ${new_value_dict} New_Request 249 | ${data} Parse XML ${requests_dir}${/}New_Request.xml keep_clark_notation=True 250 | ${text_name} Evaluate Xpath ${data} //name 251 | Should be equal ${text_name[0].text} Carlota 252 | ${text_name2} Evaluate Xpath ${data} //name2 253 | Should be equal ${text_name2[0].text} BBBB 254 | ${text_date} Evaluate Xpath ${data} //startDate 255 | Should be equal ${text_date[0].text} 2019-06-03 256 | Should be equal ${text_date[1].text} 2019-06-03 257 | Should be equal ${text_date[2].text} 2019-06-03 258 | ${text_reason} Evaluate Xpath ${data} //Reason 259 | Should be equal ${text_reason[0].text} 0000 260 | Should be equal ${text_reason[1].text} 0000 261 | Should be equal ${text_reason[2].text} 0000 262 | 263 | Test Edit XML Request 8 264 | [Tags] edit_xml 265 | [Documentation] Change all dates tags 266 | ${new_value_dict} Create Dictionary startDate=07-06-2020 267 | ${xml_edited} Edit XML Request ${requests_dir}${/}request.xml ${new_value_dict} New_Request 268 | ${data} Parse XML ${requests_dir}${/}New_Request.xml keep_clark_notation=True 269 | ${text_name} Evaluate Xpath ${data} //name 270 | Should be equal ${text_name[0].text} AAAAA 271 | ${text_name2} Evaluate Xpath ${data} //name2 272 | Should be equal ${text_name2[0].text} BBBB 273 | ${text_date} Evaluate Xpath ${data} //startDate 274 | Should be equal ${text_date[0].text} 07-06-2020 275 | Should be equal ${text_date[1].text} 07-06-2020 276 | Should be equal ${text_date[2].text} 07-06-2020 277 | ${text_reason} Evaluate Xpath ${data} //Reason 278 | Should be equal ${text_reason[0].text} 0000 279 | Should be equal ${text_reason[1].text} 0000 280 | Should be equal ${text_reason[2].text} 0000 281 | -------------------------------------------------------------------------------- /dist/robotframework-soaplibrary-0.8.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarketSquare/Robot-Framework-SOAP-Library/4c0a37b4b1f789765dae3808f81ab40535f1c0d6/dist/robotframework-soaplibrary-0.8.tar.gz -------------------------------------------------------------------------------- /dist/robotframework-soaplibrary-0.9.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarketSquare/Robot-Framework-SOAP-Library/4c0a37b4b1f789765dae3808f81ab40535f1c0d6/dist/robotframework-soaplibrary-0.9.tar.gz -------------------------------------------------------------------------------- /dist/robotframework-soaplibrary-1.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarketSquare/Robot-Framework-SOAP-Library/4c0a37b4b1f789765dae3808f81ab40535f1c0d6/dist/robotframework-soaplibrary-1.0.tar.gz -------------------------------------------------------------------------------- /dist/robotframework-soaplibrary-1.2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarketSquare/Robot-Framework-SOAP-Library/4c0a37b4b1f789765dae3808f81ab40535f1c0d6/dist/robotframework-soaplibrary-1.2.tar.gz -------------------------------------------------------------------------------- /dist/robotframework-soaplibrary-1.3.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarketSquare/Robot-Framework-SOAP-Library/4c0a37b4b1f789765dae3808f81ab40535f1c0d6/dist/robotframework-soaplibrary-1.3.tar.gz -------------------------------------------------------------------------------- /examples/request/cep.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 01305901 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/request/ip.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8.8.8.8 7 | 0 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/samples.robot: -------------------------------------------------------------------------------- 1 | *** Settings *** 2 | Library SoapLibrary 3 | Library Collections 4 | 5 | *** Variables *** 6 | ${wsdl_brazilian_post_office} https://apps.correios.com.br/SigepMasterJPA/AtendeClienteService/AtendeCliente?wsdl 7 | ${wsdl_ip_geo} http://ws.cdyne.com/ip2geo/ip2geo.asmx?wsdl 8 | 9 | *** Test Cases *** 10 | Test with Convert XML Response to Dictionary 11 | [Documentation] Test with 'Call SOAP Method With XML' and get the response values using ´Convert XML Response to Dictionary´ 12 | Create SOAP Client ${wsdl_ip_geo} 13 | ${response} Call SOAP Method With XML ${CURDIR}/request/ip.xml 14 | ${dict_response} Convert XML Response to Dictionary ${response} 15 | ${body} Get From Dictionary ${dict_response} Body 16 | ${resolveipresponse} Get From Dictionary ${body} ResolveIPResponse 17 | ${resolveipresult} Get From Dictionary ${ResolveIPResponse} ResolveIPResult 18 | ${country} Get From Dictionary ${ResolveIPResult} Country 19 | ${latitude} Get From Dictionary ${ResolveIPResult} Latitude 20 | ${longitude} Get From Dictionary ${ResolveIPResult} Longitude 21 | ${areacode} Get From Dictionary ${ResolveIPResult} AreaCode 22 | ${certainty} Get From Dictionary ${ResolveIPResult} Certainty 23 | ${countrycode} Get From Dictionary ${ResolveIPResult} CountryCode 24 | Log ${country} 25 | Log ${latitude} 26 | Log ${longitude} 27 | Log ${areacode} 28 | Log ${certainty} 29 | Log ${countrycode} 30 | 31 | Test with Get Data From XML By Tag 32 | [Documentation] Test with 'Call SOAP Method With XML' and get the response values using ´Get Data From XML By Tag´ 33 | Create SOAP Client ${wsdl_brazilian_post_office} 34 | ${response} Call SOAP Method With XML ${CURDIR}/request/cep.xml 35 | ${postal_code} Get Data From XML By Tag ${response} cep 36 | ${city} Get Data From XML By Tag ${response} cidade 37 | ${street} Get Data From XML By Tag ${response} end 38 | ${state} Get Data From XML By Tag ${response} uf 39 | Log ${postal_code} 40 | Log ${city} 41 | Log ${street} 42 | Log ${state} -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests>=2.31.0 2 | robotframework>=5.0.1 3 | urllib3>=1.26.18 4 | zeep>=4.2.1 5 | lxml>=4.9.1 -------------------------------------------------------------------------------- /robotframework_soaplibrary.egg-info/PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 2.1 2 | Name: robotframework-soaplibrary 3 | Version: 1.3 4 | Summary: SOAP Library for Robot Framework 5 | Home-page: https://github.com/Altran-PT-GDC/Robot-Framework-SOAP-Library 6 | Author: Altran Portugal 7 | Author-email: samuca@gmail.com 8 | License: MIT License 9 | Platform: UNKNOWN 10 | License-File: LICENSE.md 11 | 12 | Test library for Robot Framework to create automated test like using SOAPUI 13 | 14 | -------------------------------------------------------------------------------- /robotframework_soaplibrary.egg-info/SOURCES.txt: -------------------------------------------------------------------------------- 1 | LICENSE.md 2 | README.md 3 | setup.py 4 | SoapLibrary/SoapLibrary.py 5 | SoapLibrary/__init__.py 6 | SoapLibrary/config.py 7 | SoapLibrary/version.py 8 | robotframework_soaplibrary.egg-info/PKG-INFO 9 | robotframework_soaplibrary.egg-info/SOURCES.txt 10 | robotframework_soaplibrary.egg-info/dependency_links.txt 11 | robotframework_soaplibrary.egg-info/requires.txt 12 | robotframework_soaplibrary.egg-info/top_level.txt -------------------------------------------------------------------------------- /robotframework_soaplibrary.egg-info/dependency_links.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /robotframework_soaplibrary.egg-info/requires.txt: -------------------------------------------------------------------------------- 1 | robotframework 2 | zeep 3 | requests 4 | urllib3 5 | -------------------------------------------------------------------------------- /robotframework_soaplibrary.egg-info/top_level.txt: -------------------------------------------------------------------------------- 1 | SoapLibrary 2 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from os.path import abspath, dirname, join 3 | 4 | try: 5 | from setuptools import setup 6 | except ImportError as error: 7 | from distutils.core import setup 8 | 9 | 10 | version_file = join(dirname(abspath(__file__)), 'SoapLibrary', 'version.py') 11 | 12 | with open(version_file) as file: 13 | code = compile(file.read(), version_file, 'exec') 14 | exec(code) 15 | 16 | setup(name = 'robotframework-soaplibrary', 17 | version = '1.3', 18 | description = 'SOAP Library for Robot Framework', 19 | long_description = 'Test library for Robot Framework to create automated test like using SOAPUI', 20 | author = 'Altran Portugal', 21 | author_email = 'samuca@gmail.com', 22 | license = 'MIT License', 23 | url = 'https://github.com/Altran-PT-GDC/Robot-Framework-SOAP-Library', 24 | packages = ['SoapLibrary'], 25 | install_requires = ['robotframework', 'zeep', 'requests', 'urllib3'] 26 | ) 27 | --------------------------------------------------------------------------------