HTTP Interface Testing
-------------------------
`seldom` has many advantages in doing interface testing.
- Support HTML/XML test reports
- Support parameterization
- Support generating random data
`seldom 2.0` added support for automated testing of HTTP interfaces..
`Seldom` compatible `Requests `__ API.
+-----------------+---------------------+
| seldom | requests |
+=================+=====================+
| self.get() | requests.get() |
+-----------------+---------------------+
| self.post() | requests.post() |
+-----------------+---------------------+
| self.put() | requests.put() |
+-----------------+---------------------+
| self.delete() | requests.delete() |
+-----------------+---------------------+
Seldom VS Request+unittest
~~~~~~~~~~~~~~~~~~~~~~~~~~
Let's take a look at how unittest + requests automate interfaces:
.. code:: python
import unittest
import requests
class TestAPI(unittest.TestCase):
def test_get_method(self):
payload = {'key1': 'value1', 'key2': 'value2'}
r = requests.get("http://httpbin.org/get", params=payload)
self.assertEqual(r.status_code, 200)
if __name__ == '__main__':
unittest.main()
This is actually pretty neat.The same use case, implemented in `seldom`.
.. code:: python
# test_req.py
import seldom
class TestAPI(seldom.TestCase):
def test_get_method(self):
payload = {'key1': 'value1', 'key2': 'value2'}
self.get("http://httpbin.org/get", params=payload)
self.assertStatusCode(200)
if __name__ == '__main__':
seldom.main()
The advantages of `seldom` are assertions, logging, and reporting.
HAR TO CASE
~~~~~~~~~~~~~
For those unfamiliar with the Requests library, writing interface test cases through `seldom` can still be a bit difficult.
Thus, Cement provided the order for `har` file to turn `case`.
First, open the Fiddler tool to grab the packet and select a particular request.
Then, select the menu bar:`file` -> `Export Sessions` -> `Selected Sessions...`
.. figure:: ../image/fiddler.png
:alt:
Select the file format to export.
.. figure:: ../image/fiddler2.png
:alt:
Click on the `next` save as the `demo.har` file.
Finally, the script file `demo.py` is converted by 'seldom -h2c'.
.. code:: shell
> seldom -h2c .\demo.har
.\demo.py
2021-06-14 18:05:50 [INFO] Start to generate testcase.
2021-06-14 18:05:50 [INFO] created file: D:\.\demo.py
`demo.py` file.
.. code:: python
import seldom
class TestRequest(seldom.TestCase):
def start(self):
self.url = "http://httpbin.org/post"
def test_case(self):
headers = {"User-Agent": "python-requests/2.25.0", "Accept-Encoding": "gzip, deflate", "Accept": "application/json", "Connection": "keep-alive", "Host": "httpbin.org", "Content-Length": "36", "Origin": "http://httpbin.org", "Content-Type": "application/json", "Cookie": "lang=zh"}
cookies = {"lang": "zh"}
self.post(self.url, json={"key1": "value1", "key2": "value2"}, headers=headers, cookies=cookies)
self.assertStatusCode(200)
if __name__ == '__main__':
seldom.main()
Run Test
~~~~~~~~~~
Open Debug mode \ ``seldom.run(debug=True)`` Run use cases.
.. code:: shell
> python .\test_req.py
2021-04-29 18:19:39 [INFO] A run the test in debug mode without generating HTML report!
2021-04-29 18:19:39 [INFO]
__ __
________ / /___/ /___ ____ ____
/ ___/ _ \/ / __ / __ \/ __ ` ___/
(__ ) __/ / /_/ / /_/ / / / / / /
/____/\___/_/\__,_/\____/_/ /_/ /_/
-----------------------------------------
@itest.info
test_get_method (test_req.TestAPI) ...
----------- Request 🚀 ---------------
url: http://httpbin.org/get method: GET
----------- Response 🛬️ -------------
type: json
{'args': {'key1': 'value1', 'key2': 'value2'}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.25.0', 'X-Amzn-Trace-Id': 'Root=1-608d67ba-7948c8610ccaac8c77284b7e'}, 'origin': '113.89.239.34', 'url': 'http://httpbin.org/get?key1=value1&key2=value2'}
ok
----------------------------------------------------------------------
Ran 1 test in 0.619s
OK
This can be clearly seen through the logs/reports.
- The method requested
- request url
- Type of response
- Data for the response
Assertion
~~~~~~~~~~~
Asserting the data returned by the interface is an important part of our work in interface automation.
**assertJSON**
The interface returns the result:
.. code:: json
{
"args": {
"hobby": [
"basketball",
"swim"
],
"name": "tom"
}
}
My goal is to assert the values of the 'name' and 'hobby' parts..
.. code:: python
import seldom
class TestAPI(seldom.TestCase):
def test_assert_json(self):
payload = {'name': 'tom', 'hobby': ['basketball', 'swim']}
self.get("http://httpbin.org/get", params=payload)
assert_json = {'args': {'hobby': ['swim', 'basketball'], 'name': 'tom'}}
self.assertJSON(assert_json)
Running logs
.. code:: shell
test_get_method (test_req.TestAPI) ...
----------- Request ---------------
url: http://httpbin.org/get method: GET
----------- Response -------------
type: json
{'args': {'hobby': ['basketball', 'swim'], 'name': 'tom'}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.22.0', 'X-Amzn-Trace-Id': 'Root=1-608a896d-48fac4f6139912ba01d2626f'}, 'origin': '183.178.27.36', 'url': 'http://httpbin.org/get?name=tom&hobby=basketball&hobby=swim'}
💡 Assert data has not key: headers
💡 Assert data has not key: origin
💡 Assert data has not key: url
ok
----------------------------------------------------------------------
Ran 1 test in 1.305s
OK
`seldom` will also prompt you for fields that have not been asserted.
**assertPath**
'assertPath' is an assertion method based on 'jmespath', very powerful.
jmespath:https://jmespath.org/specification.html
The interface returns the result:
.. code:: json
{
"args":{
"hobby":
["basketball", "swim"],
"name": "tom"
}
}
Assertion using PATH:
.. code:: python
import seldom
class TestAPI(seldom.TestCase):
def test_assert_path(self):
payload = {'name': 'tom', 'hobby': ['basketball', 'swim']}
self.get("http://httpbin.org/get", params=payload)
self.assertPath("name", "tom")
self.assertPath("args.hobby[0]", "basketball")
**assertSchema**
Sometimes you don't care what the data itself is, but you need to assert the type of the data.
'assertSchema' is an assertion method based on 'JSONSchema'.
jsonschema: https://json-schema.org/learn/
The interface returns the result:
.. code:: json
{
"args": {
"hobby":
["basketball", "swim"],
"name": "tom",
"age": "18"
}
}
Assertion using `assertSchema`:
.. code:: python
import seldom
class TestAPI(seldom.TestCase):
def test_assert_schema(self):
payload = {"hobby": ["basketball", "swim"], "name": "tom", "age": "18"}
self.get("/get", params=payload)
schema = {
"type": "object",
"properties": {
"args": {
"type": "object",
"properties": {
"age": {"type": "string"},
"name": {"type": "string"},
"hobby": {
"type": "array",
"items": {
"type": "string"
},
}
}
}
},
}
self.assertSchema(schema)
Again, the assertions provided by `seldom` are very flexible and powerful.
Interface Data Dependency
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In scenario testing, we need to call the next interface using data from the previous interface.
**Sample 1**
.. code:: python
import seldom
class TestRespData(seldom.TestCase):
def test_data_dependency(self):
"""
Test for interface data dependencies
"""
headers = {"X-Account-Fullname": "bugmaster"}
self.get("/get", headers=headers)
self.assertStatusCode(200)
username = self.response["headers"]["X-Account-Fullname"]
self.post("/post", data={'username': username})
self.assertStatusCode(200)
\ ``self.response``\ Used to record the result returned by the last interface, just use it.
**Sample 2**
Defining common modules
.. code:: python
# common.py
from seldom import HttpRequest
class Common(HttpRequest):
def get_login_user(self):
"""
Call the interface to get the user name
"""
headers = {"X-Account-Fullname": "bugmaster"}
self.get("http://httpbin.org/get", headers=headers)
user = self.response["headers"]["X-Account-Fullname"]
return user
Create classes that inherit `HttpRequest` class calls using Http request methods 'get/post/put/delete'.
Referencing public modules
.. code:: python
import seldom
from common import Common
class TestRequest(seldom.TestCase):
def start(self):
self.c = Common()
def test_case(self):
# get_login_user()
user = self.c.get_login_user()
print(user)
self.post("http://httpbin.org/post", data={'username': user})
self.assertStatusCode(200)
if __name__ == '__main__':
seldom.main(debug=True)
Data-Driver
~~~~~~~~~~~~~
`seldom` has a strong data-driven nature and is very convenient for interface testing.
**@data**
.. code:: python
import seldom
from seldom import data
class TestDDT(seldom.TestCase):
@data([
("key1", 'value1'),
("key2", 'value2'),
("key3", 'value3')
])
def test_data(self, key, value):
"""
Data-Driver Tests
"""
payload = {key: value}
self.post("/post", data=payload)
self.assertStatusCode(200)
self.assertEqual(self.response["form"][key], value)
**@file\_data**
data file:
.. code:: json
{
"login": [
["admin", "admin123"],
["guest", "guest123"]
]
}
code file:
.. code:: python
import seldom
from seldom import file_data
class TestDDT(seldom.TestCase):
@file_data("data.json", key="login")
def test_data(self, username, password):
"""
Data-Driver Tests
"""
payload = {username: password}
self.post("http://httpbin.org/post", data=payload)
self.assertStatusCode(200)
self.assertEqual(self.response["form"][username], password)
More like data files(csv/excel/yaml),\ `View `__
Random Test Data
~~~~~~~~~~~~~~~~~~
SELDOM provides a method of randomly generating test data to generate some commonly used data.
.. code:: python
import seldom
from seldom import testdata
class TestAPI(seldom.TestCase):
def test_data(self):
phone = testdata.get_phone()
payload = {'phone': phone}
self.get("http://httpbin.org/get", params=payload)
self.assertPath("args.phone", phone)
For more types of test data, `View `__