This project implements the interface test of Daily Cost :
- Python+Requests sends and processes HTTP protocol request interface
- Pytest as a test executor
- YAML management test data
- Allure to generate test reports.
This project is based on pytestDemo to do its own implementation.
1. Project structure and installation and deployment
project structure
- api : interface encapsulation layer, such as encapsulating HTTP interface as Python interface
- commom : various tools for reading data from files
- core : requests request method encapsulation, keyword return result class
- config : environment, database connection and other configurations
- data : test data file management
- operation : keyword encapsulation layer, such as encapsulating multiple Python interfaces as keywords
- pytest.ini : pytest configuration file
- requirements.txt : related dependency package files
- testcases : test cases
- api_test : single interface test
- scenario_test : scenario/business process test
Installation and deployment
- Deploying the Daily Cost App: Tutorial
- Download the source code of this test project , install the corresponding dependencies through pip
pip3 install -r requirements.txt
, and modify the corresponding content in setting.ini according to the actual situation - Enter
pytest
run test - If you want to use Allure to view the generated test report, you need to install the Allure service first:
brew install allure
2. Environment configuration
- Defined in the config-setting.ini file
api_root_url
- Implement the load_ini() method in common-read_data.py to read the configuration file
api_root_url
Call it where it needs to be usedload_ini(data_file_path)
to read the corresponding value
3. Encapsulate HTTP request
- Encapsulate requests request method: encapsulate methods such as GET, POST, PUT, DELETE sent in requests into the RestClient class
- Define the tested API interface Request: Create a collection class corresponding to the request according to the domain in the api folder, such as
class User(RestClient)
, and define each interface information, such as the login interface:
def login(self, **kwargs):
return self.post("/login", **kwargs)
4. Keyword encapsulation
Keywords should be of some business significance. When encapsulating keywords, you can only encapsulate one interface, or call multiple interfaces to complete.
For example, if we want to test a record , the interface will only return the ID of the bill after the interface is called successfully, and we also need to call the query bill details interface to help determine whether each field is consistent with the input, then we can test like this:
- First,
记一笔-查看明细
encapsulate the operation as a keyword, in this keyword, call the record and query the bill detail results in turn, and you can customize the return result of the keyword - Then, when writing a test case, call the keyword directly to test, then you can return the result of the keyword, and when asserting, you can also directly assert the result returned by the keyword
Another example is to query the statistical results of monthly bills . One interface can independently complete the business query operation. We only need to call this interface in the keyword.
Back to this project, the specific code logic is as follows:
- In the operation folder, the keywords are organized according to the domain file
- Define the data structure class returned by the keyword in core
ResultBase
- Define keywords. When calling the corresponding API request, you need to explicitly pass the interface request parameters and define the return result
def bill_monthly_stat(date, token):
header = {
"Content-Type": "application/json",
"Authorization": "Bearer " + token
}
param = {"date": date}
res = bill_details.get_bill_monthly(params=param, headers=header)
return ResultBase(res)
There is only one request encapsulated in the keywords in this project.
5. Single interface test
Take 记一笔
interfaces as an example.
- Define interface and keywords
// api -> bill.py : 定义接口
def create_new_bill(self, **kwargs):
return self.post("/bill", **kwargs)
// operation -> bill.py : 定义关键字
def bill_create(category_id, type, amount, note, date, token):
payload = {
"categoryId": category_id,
"type": type,
"amount": amount,
"note": note,
"date": date
}
header = {
"Content-Type": "application/json",
"Authorization": "Bearer " + token
}
res = bill.create_new_bill(json=payload, headers=header)
return ResultBase(res)
- Interface test class
// testcases -> api_test -> bill -> test_bill_create.py
class TestBillCreate():
@mark.smoke
@mark.parametrize('category_id, type, amount, note, date, status_code, message',
[
("5442d3b8-9d4a-4654-bf0b-d2249efef190", "EXPENSE", 100.01, "note1111test", "2021-12-01", 200, "操作成功"),
("66c22fad-be9d-481d-a445-c57d266bf938", "INCOME", 1000.01, "note1111test", "2021-12-01", 200, "操作成功")
])
def test_bill_create_success(self, category_id, type, amount, note, date, status_code, message, token):
result = bill_create(category_id, type, amount, note, date, token)
assert result.response.status_code == status_code
assert result.message == message
assert result.response.json()["data"]["id"]
@mark.smoke
It uses the mark marking function of pytest. When running, use the commandpytest -m smoke
to run only the use cases marked as smoke@mark.parametrize
It is the variable parameterization function of pytest, which can realize data-driven testing. As shown above, if there are two parameterized data, the use case will be executed twice with these two data- There is a parameter when calling bill_create
token
, which is actuallyconftest.py
defined in the file - The assert in pytest uses python's built-in assertion library
6. YAML file management test data
In the above example, we @mark.parametrize
directly wrote the specific test data above the use case when using it. In order to improve the efficiency of later modification and maintenance, we usually separate test data and use cases, that is, we can use yaml files to manage test data separately, and conftest.py can help read data.
- Define the yaml file: data -> api_test_data.yml
test_bill_create_success:
# category_id, type, amount, note, date, status_code, message
- ["5442d3b8-9d4a-4654-bf0b-d2249efef190", "EXPENSE", 100.01, "note1111test", "2021-12-01", 200, "操作成功"]
- ["66c22fad-be9d-481d-a445-c57d266bf938", "INCOME", 1000.01, "note1111test", "2021-12-01", 200, "操作成功"]
key
The function name corresponding to the test casetest_bill_create_success
-
Indicates that value is an arrayconftest.py
Read the corresponding test data in
import pytest
import os
from common.read_data import data
BASE_PATH = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
def get_data(yaml_file_name):
try:
data_file_path = os.path.join(BASE_PATH, "data", yaml_file_name)
yaml_data = data.load_yaml(data_file_path)
except Exception as ex:
pytest.skip(str(ex))
else:
return yaml_data
api_data = get_data("api_test_data.yml")
- Read data from yaml file when parameterizing
@mark.parametrize('category_id, type, amount, note, date, status_code, message',
api_data["test_bill_create_success"])
7. API scenario testing
When doing API testing, in addition to the verification of the input and output of a single interface, the test of business scenarios composed of multiple interfaces connected in series is also essential.
To put it simply, let’s take the home page after logging in as an example. The interface call is: Login -> Query the current month’s bill list -> Query the current month’s bill statistics.
- Define the scenario test class
// testcases -> scenario_test -> test_get_one_month_bill.py
import allure
import pytest
from operation.user import login_user
from operation.bill import one_month_bill_list_get_by_date, bill_monthly_stat
@pytest.mark.core
class TestGetOneMonthBill:
@allure.title("01: user[yuxiaomeng] login")
@allure.story('story_1')
def test_user_login(self, core_env):
result = login_user('yuxiaomeng', '20211030.y')
assert result.status_code == 200
assert result.data["token"]
core_env["token"] = result.data["token"]
@allure.title("02: get homepage info - bill details list")
@allure.story('story_1')
def test_get_current_month_monthly_bill_list(self, core_env):
result = one_month_bill_list_get_by_date(core_env["date"], core_env["token"])
assert result.status_code == 200
assert len(result.data) == 2
assert result.data[0]["date"] == "2021-11-11"
assert result.data[0]["expense"] == 510.5
@allure.title("03: get homepage info - bill monthly statistics ")
@allure.story('story_1')
def test_get_current_month_bill(self, core_env):
result = bill_monthly_stat(core_env["date"], core_env["token"])
assert result.status_code == 200
assert result.data["expense"]
- Which
core_env
is used to pass test data between interfaces
// testcases -> scenario_test -> conftest.py
import pytest
@pytest.fixture(scope='session')
def core_env():
return {"date": "2021-11"}
8. Allure generates a test report
For detailed usage of allure-pytest, please refer to the official documentation . Only a brief introduction is given here.
- Add the --alluredir parameter when running the test to save the report to the specified folder:
pytest --alluredir=/tmp/my_allure_results
- View the report after running:
allure serve /tmp/my_allure_results
The following is the supporting information. For friends who do [software testing], it should be the most comprehensive and complete preparation warehouse. This warehouse also accompanied me through the most difficult journey. I hope it can help you too!
Software testing interview applet
The software test question bank maxed out by millions of people! ! ! Who is who knows! ! ! The most comprehensive quiz mini program on the whole network, you can use your mobile phone to do the quizzes, on the subway or on the bus, roll it up!
The following interview question sections are covered:
1. Basic theory of software testing, 2. web, app, interface function testing, 3. network, 4. database, 5. linux
6. web, app, interface automation, 7. performance testing, 8. programming basics, 9. hr interview questions, 10. open test questions, 11. security testing, 12. computer basics
Information acquisition method: