Seldom 2.0 - making interface automation testing easier

Posted by phpcharan on Wed, 26 Jan 2022 03:57:35 +0100

preface

HTTP interface testing is very simple. No matter tools, frameworks or platforms, as long as a few good points are good tools.

  1. Test data problems: for example, deleting interfaces and repeated execution can keep the results consistent. Data initialization must be done.
  2. Interface dependency: interface B depends on the return value of interface A, and interface C depends on the return value of interface B.
  3. Encryption: different interfaces have different encryption rules. Some use timestamp, md5, base64 and AES. How to provide these capabilities.
  4. Assertion problem: the structure returned by some interfaces is very complex. How to assert flexibly.

For the above problems, tools and platforms either do not support or are troublesome, but the framework is the most flexible.

unittest/pytest + requests/https just write the code directly, which is simple and flexible.

So it's also to write code. Framework A needs 10 lines and framework B only needs 5 lines, but it doesn't fail. Of course, I choose fewer. After all, life is short.

seldom is suitable for personal interface automation projects. It has the following advantages.

  • You can write less code
  • Automatically generate HTML/XML test reports
  • Support parameterization and reduce duplicate code
  • Support generating random data
  • Support conversion of har file to case
  • Support database operation

These are the functions supported by seldom. We only need to integrate the HTTP interface library and provide powerful assertions. seldom 2.0} adds automatic test support for HTTP interface.

Seldom's compatible Requests API is as follows:

seldomrequests
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 are used for interface automation:

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 very concise. The same use case is implemented with 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 main simplification point is the processing of the returned data of the interface. Of course, seldom's real advantages are assertions, logging, and reporting.

har to case

For those who are not familiar with the Requests library, it is still a little difficult to write interface test cases through seldom. Thus, seldom provides the command to convert the har file to case.

First, open fiddler tool to capture packets and select a request.

Then, select the menu bar: file - > Export Sessions - > Selected Sessions

Select the file format to export.

Click next # to save as demo Har file.

Finally, it is converted to demo through seldom -h2c} Py} script file.

> 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) runs the above use case.

> 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.22.0', 'X-Amzn-Trace-Id': 'Root=1-608a883c-7b355ba81fcd0d287566405a'}, 'origin': '183.178.27.36', 'url': 'http://httpbin.org/get?key1=value1&key2=value2'}
ok

----------------------------------------------------------------------
Ran 1 test in 0.619s

OK

It can be clearly seen through logs / reports.

  • Requested method
  • Request url
  • Type of response
  • Response data

Stronger assertions

Asserting the data returned by the interface is a very important work of interface automation.

assertJSON

The interface returns the following results:

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

My goal is to assert the contents of the name , and , hobby , sections. seldom can assert against JSON files.

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)

Run log

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 which fields are not asserted.

assertPath

The data returned by the interface is as follows:

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

In seldom, you can assert through 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 data. assertSchema {is an assertion method based on jsonschema implementation.

jsonschema: Learn | JSON Schema  

The data returned by the interface is as follows:

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

In seldom, assertions can be made by using JSON schema:

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)

Feel again that the assertions provided by seldom are very flexible and powerful.

Interface data dependency

In scenario testing, we need to use the data of the previous interface to call the next interface.

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)

seldom provides self Response is used to record the results returned by the previous interface and can be used directly.

Data driven

seldom originally provides a powerful data driver, which 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

Create data JSON data file

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

Via file_data to achieve data-driven.

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)

Change the data file (csv/excel/yaml), refer to

Randomly generate test data

seldom provides the method of randomly generating test data, which can generate some common 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, refer to

Database operation

seldom supports sqlite3 and MySQL database operations.

sqlite3MySQL
delete_data()delete_data()
insert_data()insert_data()
select_data()select_data()
update_data()update_data()
init_table()init_table()
close()close()

Connect to database

Connecting to sqlit3 database

from seldom.db_operation import SQLiteDB

db = SQLiteDB(r"D:\learnAPI\db.sqlite3")

Connect to MySQL database (required)

  1. Install pymysql driver
> pip install pymysql
  1. link
from seldom.db_operation import MySQLDB

db = MySQLDB(host="127.0.0.1", 
             port="3306", 
             user="root", 
             password="123", 
             database="db_name")

Operation method

  • delete_data

Delete table data.

db.delete_data(table="user", where={"id":1})
  • insert_data

Insert a piece of data.

data = {'id': 1, 'username': 'admin', 'password': "123"},
db.insert_data(table="user", data=data)
  • select_data

Query table data.

result = db.select_data(table="user", where={"id":1, "name": "tom"})
print(result)
  • update_data

Update table data.

db.update_data(table="user", data={"name":"new tom"}, where={"name": "tom"})
  • init_table

To insert data in batches, clear the table data before inserting.

datas = {
    'api_event': [
        {'id': 1, 'name': 'Red rice Pro release conference'},
        {'id': 2, 'name': 'The number of participants is 0'},
        {'id': 3, 'name': 'The current status is 0 closed'},
        {'id': 4, 'name': 'Press conference is over'},
        {'id': 5, 'name': 'Xiaomi 5 press conference'},
    ],
    'api_guest': [
        {'id': 1, 'real_name': 'alen'},
        {'id': 2, 'real_name': 'has sign'},
        {'id': 3, 'real_name': 'tom'},
    ]
}

db.init_table(datas)
  • close

Close the database connection.

db.close()

Finally, the project of implementing interface automation test based on seldom: https://github.com/defnngj/pyrequest2

welfare

Welfare release, from introduction to actual combat, from e-books to real interview questions, should be the most comprehensive and complete war preparation warehouse for [software testing] friends. This warehouse has also accompanied me through the most difficult journey. I hope it can also help you.

If you need this learning material, you can scan WeChat's official CSDN official account below (100% free access).

 

 

Topics: Python Operation & Maintenance