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:

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.

# 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…

Select the file format to export.

Click on the next save as the demo.har file.

Finally, the script file demo.py is converted by ‘seldom -h2c’.

> 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.

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.

> 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:

{
  "args": {
    "hobby": [
      "basketball",
      "swim"
    ],
    "name": "tom"
  }
}

My goal is to assert the values of the ‘name’ and ‘hobby’ parts..

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

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:

{
  "args":{
    "hobby":
      ["basketball", "swim"],
    "name": "tom"
  }
}

Assertion using PATH:

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:

{
  "args": {
    "hobby":
      ["basketball", "swim"],
    "name": "tom",
    "age": "18"
  }
}

Assertion using assertSchema:

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

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.responseUsed to record the result returned by the last interface, just use it.

Sample 2

Defining common modules

# 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

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

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:

{
 "login":  [
    ["admin", "admin123"],
    ["guest", "guest123"]
 ]
}

code file:

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.

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