2W薪资必备技能 —— Python接口自动化测试

news2024/9/21 2:33:44

目录

一、基础准备

1. 环境搭建

2. 接口基础知识

2.1 接口分类

2.2 接口请求类型

二、Requests 快速上手

1. requests基础

1.1 发送请求

1.2 参数传递

1.3 接口响应

1.4 接口其他处理

2. requests 高级应用

2.1 会话对象

2.2 请求与响应对象

2.3 准备的请求

2.4 SSL证书验证

2.5 客户端证书

三、接口测试实战

1. 百度翻译接口测试

2. urllib请求接口

四、搭建测试接口平台

1. 环境搭建

1.1 项目创建

1.2 接口开发配置

2. 接口开发

2.1 查询文章接口

2.2 增加文章接口

2.3 修改文章接口

2.4 删除文章接口

2.5 token认证

2.6 接口测试

五、接口自动化

1. 数据处理

1.1 Excel中数据

1.2 JSON中数据

1.3 数据库中的数据

2. 邮件告警

3. 封装测试

3.1 多种请求方式兼容

3.2 自动化封装

3. 执行自动化用例

4. 持续集成

4.1 Jenkins环境搭建

4.2 接口继承

4.3 接口自动化集成

绵薄之力【软件测试全套资源分享】

最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走​编辑


一、基础准备

1. 环境搭建

  工欲善其事必先利其器,废话不多说。我们先开始搭建环境。

# 创建项目目录
mkdir InterfaceTesting

# 切换到项目目录下
cd InterfaceTesting

# 安装虚拟环境创建工具
pip install virtualenv

# 创建虚拟环境,env代表虚拟环境的名称,可自行定义
virtualenv env

# 启动虚拟环境,执行下面命令后会发现路径上有 (env) 字样的标识
source env/Scripts/activate

# 查看 (env) 环境下使用的 Python 和 pip 工具版本
ls env/Scripts/

# *** 安装 requests ***
pip install requests

# 退出虚拟环境,退出后路径上的 (env) 字样的标识消失
cd env/Scripts/
deactivate

# 导出环境所需要的模块的清单
pip freeze >> requirements.txt

# 上传 GitHub 时,将下面项忽略上传
echo env/ >> .gitignore
echo InterfaceTesting.iml >> .gitignore
echo __pycache__/ >> .gitignore

# 将代码传至 GitHub
# 本地仓初始化
git init
# 创建本地仓与 GitHub 仓的远程链接
git remote add github 你的github仓的地址
# 将代码添加到暂存区
git add .
# 将代码提交到 
git commit -m "init environment"
# 将代码上传到GitHub仓中
git push github master
复制代码

初始化环境的项目结构示例如下:

 

2. 接口基础知识

2.1 接口分类

接口一般来说有两种,一种是程序内部的接口,一种是系统对外的接口。

(1) webservice接口:走soap协议通过http传输,请求报文和返回报文都是xml格式的,我们在测试的时候都要通过工具才能进行调用,测试。
(2) http api 接口:走http协议,通过路径来区分调用的方法,请求报文都是key-value形式的,返回报文一般都是json串,有get和post等方法。
复制代码

2.2 接口请求类型

根据接口的请求方法,常用的几种接口请求方式:

(1) GET:从指定资源获取数据
(2) POST:向指定的资源请求被处理的数据(例如用户登录)
(3) PUT:上传指定的URL,一般是修改,可以理解为数据库中的 update
(4) DELETE:删除指定资源
复制代码

二、Requests 快速上手

1. requests基础

  所有的数据测试目标以一个开源的接口模拟网站【HTTPBIN】为测试对象。

1.1 发送请求

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File    :   requests_send_request.py
@Time    :   2019/9/2 11:54
@Author  :   Crisimple
@Github :    https://crisimple.github.io/
@Contact :   Crisimple@foxmail.com
@License :   (C)Copyright 2017-2019, Micro-Circle
@Desc    :   None
"""

import requests

# 1.requests请求方式
# (1) GET请求方式
httpbin_get = requests.get('http://httpbin.org/get', data={'key': 'value'})
print('httpbin_get: ', httpbin_get.text)

# (2) POST请求方式
httpbin_post = requests.post('https://httpbin.org/post', data={'key': 'value'})
print('httpbin_post: ', httpbin_post.text)

# (3) PUT请求方式
 httpbin_put = requests.put('https://httpbin.org/put', data={'key': 'value'})
print('httpbin_put: ', httpbin_put.text)

# (4) DELETE请求方式
httpbin_delete = requests.delete('https://httpbin.org/delete', data={'key': 'value'})
print('httpbin_delete', httpbin_delete)

# (5) PATCH亲求方式
httpbin_patch = requests.patch('https://httpbin.org/patch', data={'key': 'value'})
print('httpbin_patch', httpbin_patch)

复制代码

1.2 参数传递

  常用的参数传递形式有四种:【GitHub示例

(1)字典形式的参数:payload = {'key1': 'value1', 'key2': 'value2'}
 (2) 元组形式的参数:payload = (('key1', 'value1'), ('key2', 'value2'))
 (3) 字符串形式的参数:payload = {'string1', 'value1'}
 (4) 多部份编码的文件:files = {
    # 显示设置文件名、文件类型和请求头
    'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})
}
复制代码
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File    :   requests_transfer_parameter.py
@Time    :   2019/9/2 12:39
@Author  :   Crisimple
@Github :    https://crisimple.github.io/
@Contact :   Crisimple@foxmail.com
@License :   (C)Copyright 2017-2019, Micro-Circle
@Desc    :   参数传递:字典、元组、字符串、文件
"""
import requests

# 2. 参数传递
# (1) 传参参数为字典形式: 数据字典会在发送请求时会自动编码为表单形式
def transfer_dict_parameter():
    payload = {
        'key1': 'value1',
        'key2': 'value2'
    }

    transfer_dict_parameter_result = requests.post('https://httpbin.org/post', params=payload)
    print("transfer_dict_parameter_url: ", transfer_dict_parameter_result.url)
    print("transfer_dict_parameter_text: ", transfer_dict_parameter_result.text)


transfer_dict_parameter()


# (2) 传参参数为元组形式: 应用于在表单中多个元素使用同一 key 的时候
def transfer_tuple_parameter():
    payload = (
        ('key1', 'value1'),
        ('key1', 'value2')
    )

    transfer_tuple_parameter_result = requests.post('https://httpbin.org/post', params=payload)
    print('transfer_tuple_parameter_url: ', transfer_tuple_parameter_result.url)
    print('transfer_tuple_parameter_text: ', transfer_tuple_parameter_result.text)


transfer_tuple_parameter()


# (3) 传参参数形式是字符串形式
def transfer_string_parameter():
    payload = {
        'string1': 'value'
    }

    transfer_string_parameter_result = requests.post('https://httpbin.org/post', params=payload)
    print('transfer_string_parameter_url: ', transfer_string_parameter_result.url)
    print('transfer_string_parameter_text: ', transfer_string_parameter_result.text)


transfer_string_parameter()


# (4) 传参参数形式:一个多部分编码(Multipart-Encoded)的文件
def transfer_multipart_encoded_file():
    interface_url = 'https://httpbin.org/post'
    files = {
        # 显示设置文件名、文件类型和请求头
        'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})
    }

    transfer_multipart_encoded_file_result = requests.post(url=interface_url, files=files)
    print('transfer_multipart_encoded_file_result_url: ', transfer_multipart_encoded_file_result.url)
    print('transfer_multipart_encoded_file_result_url: ', transfer_multipart_encoded_file_result.text)


transfer_multipart_encoded_file()
复制代码

1.3 接口响应

  给接口传递参数,请求接口后,接口会给我们我们响应返回,接口在返回的时候,会给我们返回一个状态码来标识当前接口的状态。

(1)状态码

GitHub示例

状态码状态描述
1xx----**信息类的状态码 **
100Continue服务器仅接收到部分请求,但是一旦服务器并没有拒绝该请求,客户端应该继续发送其余的请求。
101Switching Protocols服务器转换协议,服务器将遵从客户的请求转换到另外一种协议
2xx----**成功类的状态码 **
200OK请求成功(是对 GET 或 POST 的请求应答文档)
201Created请求被创建完成,同时信的资源被创建
202Accepted供处理的请求已被接收,但是处理未完成
203Non-authoritative Information文档已正常地返回,但一些应答头可能不正确,以为使用的式文档的拷贝
204No Content没有新文档。浏览器应该继续显示原来的文档。如果用户定期地刷新页面,而Servlet可以确定用户文档足够新,这个状态代码是很有用的。
205Reset Content没有新文档。但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容。
206Partial Content客户发送了一个带有Range头的GET请求,服务器完成了它。
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File    :   response_code.py
@Time    :   2019/9/2 15:41
@Author  :   Crisimple
@Github :    https://crisimple.github.io/
@Contact :   Crisimple@foxmail.com
@License :   (C)Copyright 2017-2019, Micro-Circle
@Desc    :   None
"""
import requests


# 1. 返回接口状态码:200
def response_200_code():
    interface_200_url = 'https://httpbin.org/status/200'
    response_get = requests.get(interface_200_url)
    response_get_code = response_get.status_code
    print('response_get_code: ', response_get_code)


response_200_code()


# 2.返回接口状态码:400
def response_400_code():
    interface_400_url = 'https://httpbin.org/status/400'
    response_get = requests.get(interface_400_url)
    response_get_code = response_get.status_code
    print('response_get_code: ', response_get_code)


response_400_code()
复制代码

(2)响应头

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File    :   response_content.py
@Time    :   2019/9/2 15:41
@Author  :   Crisimple
@Github :    https://crisimple.github.io/
@Contact :   Crisimple@foxmail.com
@License :   (C)Copyright 2017-2019, Micro-Circle
@Desc    :   None
"""
import requests


# 1. 返回接口状态码:
# (1). 返回接口状态码:200
def response_200_code():
    interface_200_url = 'https://httpbin.org/status/200'
    response_get = requests.get(interface_200_url)
    response_get_code = response_get.status_code
    print('response_get_code: ', response_get_code)


response_200_code()


# (2).返回接口状态码:400
def response_400_code():
    interface_400_url = 'https://httpbin.org/status/400'
    response_get = requests.get(interface_400_url)
    response_get_code = response_get.status_code
    print('response_get_code: ', response_get_code)


response_400_code()


# (3) 重定向接口返回状态码:301
def response_301_code():
    interface_url = 'https://butian.360.cn'
    response_get = requests.get(interface_url)
    response_get_code = response_get.status_code
    print('response_get_code: ', response_get_code)


response_301_code()


# ------------------------------------------------------
# 2. 响应内容
  响应内容的请求头、查看文本、编码方式、二进制响应、原始响应。
def response_contents():
    url = 'https://httpbin.org/get'

    response_get = requests.get(url=url)

    # 响应头
    print('response_get_headers', response_get.headers)

    # 响应文本
    print('response_get_text: ', response_get.text)

    # 文本编码方式
    print('response_get_encoding: ', response_get.encoding)

    # 二进制响应内容
    print('response_get_content: ', response_get.content)

    # 原始响应内容
    origin_content = response_get.raw
    origin_content_read = origin_content.read(10)
    print('origin_content: ', origin_content)
    print('origin_content_read: ', origin_content_read)


response_contents()
复制代码

1.4 接口其他处理

GitHub示例

(1) 操作cookies

import requests
import time

url = 'https://httpbin.org/get'

def operator_cookies():
    r = requests.get(url)
    print('r.cookies: ', r.cookies)

    jar = requests.cookies.RequestsCookieJar()
    jar.set('tasty_cookie', 'yum', domain='httpbin.org', path='/cookies')
    jar.set('gross_cookie', 'blech', domain='httpbin.org', path='/elsewhere')
    r2 = requests.get(url=url, cookies=jar)
    print('r2.text', r2.text)


operator_cookies()
复制代码

(2) 请求历史

import requests

url = 'https://httpbin.org/get'


def request_history():
    r = requests.get(url=url)
    print('r.history: ', r.history)


request_history()
复制代码

(3) 超时请求

  requests 在经过 timeout 参数设定的秒数时间之后停止等待响应。

import requests
import time

def timeout():
    print(time.time())
    url = 'https://httpbin.org/get'
    print(time.time())
    r = requests.get(url, timeout=5)
    print(time.time())


timeout()
复制代码

(4) 错误与异常

  常见的错误异常有:

· 遇到网络问题(如:DNS 查询失败、拒绝连接等时),requests 会抛出一个 ConnectionError 异常。
· 如果 HTTP 请求返回了不成功的状态码, Response.raise_for_status() 会抛出一个 HTTPError异常。
· 若请求超时,则超出一个 Timeout 异常。
· 若请求超过了设定的最大重定向次数,则会抛出一个 TooManyRedirects 异常。
· 所有 Requests 显式抛出的异常都继承自 requests.exceptions.RequestsException。
复制代码

2. requests 高级应用

2.1 会话对象

2.2 请求与响应对象

2.3 准备的请求

2.4 SSL证书验证

2.5 客户端证书


三、接口测试实战

1. 百度翻译接口测试

  理论千千万万,实战才是真理。百度翻译提供了一套成熟的翻译接口(不是恰饭😂),我们就用此接口对前面理论进行实战。【GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File    :   baidu_translate.py
@Time    :   2019/9/2 20:05
@Author  :   Crisimple
@Github :    https://crisimple.github.io/
@Contact :   Crisimple@foxmail.com
@License :   (C)Copyright 2017-2019, Micro-Circle
@Desc    :   None
"""
import requests
import random
import hashlib
import urllib
import json


class BaiduTranslate(object):
    def __init__(self, word):
        # 你要翻译的元素
        self.q = word
        self.fromLang = 'en'
        self.toLang = 'zh'
        self.baidu_translate = 'https://api.fanyi.baidu.com'
        self.translate_api_url = '/api/trans/vip/translate'

        # 百度开发者配置信息
        self.appid = 'XXXXXXXX'
        self.secretKey = 'XXXXXXXX'

        # 开发配置
        self.salt = random.randint(32768, 65536)
        self.sign = self.appid + self.q + str(self.salt) + self.secretKey
        m1 = hashlib.md5()
        m1.update(self.sign.encode('utf-8'))
        self.sign = m1.hexdigest()
        self.my_url = self.translate_api_url + '?appid=' + self.appid + '&q=' + urllib.request.quote(self.q) + '&from=' + self.fromLang + '&to=' + self.toLang + '&salt=' + str(self.salt) + '&sign=' + self.sign

    def en_translate_zh(self):
        re = requests.request('post', self.baidu_translate + self.translate_api_url)
        print('\n\t re.text', re.text)
        re_json = json.loads(re.text)
        print('\n\t re_json', re_json)


if __name__ == "__main__":
    bt = BaiduTranslate('test')
    bt.en_translate_zh()
复制代码

2. urllib请求接口

  有了requests库请求接口了,为什么要再用urllib来请求接口呢?因为urllib是python的基础库,不需要下载安装,在对环境要求甚高的环境下,在不破坏原来的环境下,依然可以让自动化代码依然运行。【GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File    :   urllib_request.py
@Time    :   2019/9/2 20:49
@Author  :   Crisimple
@Github :    https://crisimple.github.io/
@Contact :   Crisimple@foxmail.com
@License :   (C)Copyright 2017-2019, Micro-Circle
@Desc    :   None
"""

from urllib import request
from urllib import parse


def urllib_request():
    base_url = 'http://www.tuling123.com/openapi/api'
    payload = {
        'key1': 'Your',
        'key2': '你好'
    }

    ur = request.Request(url=base_url)
    ur_response = request.urlopen(ur)
    print('\n ur_response: \n\t', ur_response)
    print('\n ur_response_getcode: \n\t ', ur_response.getcode)
    print('\n ur_response_headers: \n\t ', ur_response.headers)

    data = parse.urlencode(payload).encode('utf-8')
    url_payload = request.Request(url=base_url, data=data)
    url_payload_response = request.urlopen(url_payload)

    print('\n url_payload_response: \n\t', url_payload_response)
    print('\n url_payload_response_getcode: \n\t ', url_payload_response.getcode)
    print('\n url_payload_response_headers: \n\t ', url_payload_response.headers)
    print('\n url_payload_response_msg: \n\t ', url_payload_response.msg)
    print('\n url_payload_response_read: \n\t ', url_payload_response.read)


urllib_request()

复制代码

四、搭建测试接口平台

  自搭建的接口平台使用Django框架进行开发,基于当前接口的需求(接口的增、删、改、查)功能,搭建一个满足需要的接口测试平台。

1. 环境搭建

1.1 项目创建

GitHub示例

 

# 下载 django 框架库
pip install django

# 创建 django 工程
django-admin startproject InterfaceTestingMock

# 创建 api_crud app
cd InterfaceTestingMock
python manage.py startapp interface_crud

# 创建 api_mock 工程的虚拟运行环境
viutualenv env

# 激活虚拟环境
source env/Scripts/activate

# 退出虚拟环境
deactivate

# 导出虚拟环境 env 所需要的库
pip freeze >> requirements.txt
复制代码

1.2 接口开发配置

(1) 创建表结构

python  manage.py migrate
复制代码

(2) 编写模型层代码,以下语句相当于创建了两张表:User,Article

# interface_crud.models.py

from django.db import models

# Create your models here.


class User(models.Model):
    id = models.AutoField(primary_key=True)
    user_name = models.CharField(max_length=50)
    user_password = models.CharField(max_length=100)
    # active inactive
    status = models.CharField(max_length=10)


class Article(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=50)
    content = models.TextField()
    # delete alive
    status = models.CharField(max_length=10)

复制代码

(3) 新增表,执行下面语句让 django 知道表发生了变化

python manage.py makemigrations interface_crud
复制代码

(4) 再次创建表

python manage.py migrate
复制代码

(5) 生成创建超级管理员账号

# 依次数据用户名、邮箱地址、密码、重复密码、确认(y)
python manage.py createsuperuser
复制代码

(6) 配置接口请求地址

# InterfaceTestingMock.urls.py
from django.contrib import admin
from django.urls import path
from interface_crud.views import add_article, modify_article

urlpatterns = [
    path('admin/', admin.site.urls),
    path('articles/', add_article),
    path('articles<int: art_id>', modify_article)
]
复制代码

2. 接口开发

&emsp:&emsp:就目前常用的接口参数传参形式分别有:表单类接口传参,多用于提供给前端页面(后续学习跟进总结);另一种常用的就是 json 传参形式的,这种传参形式能够满足开发处业务逻辑更为复杂的接口,本次接口开发就采用该形式。【GitHub示例】---【GitHub示例

备注:2.1-2.6是根据【** [秦无殇的博客](https://www.cnblogs.com/webDepOfQWS/p/10693152.html)**】学习整理而来,谢谢这位老哥❀

2.1 查询文章接口

 

from interface_crud.models import Article
from django.http import JsonResponse, HttpResponse
import json

# Create your views here.


# 查询文章
def query_article(request):
    if request.method == 'GET':
        articles = {}
        query_articles = Article.objects.all()
        print('query_articles: ', query_articles)
        for title in query_articles:
            articles[title.title] = title.status
        return JsonResponse({"status": "BS.200", "all_titles": articles, "msg": "query articles success."})
        print("request.body", request.body)
    else:
        return HttpResponse("方法错误")
复制代码

2.2 增加文章接口

 

# 增加文章
def add_article(request):
    auth_res = user_auth(request)
    if auth_res == "auth_fail":
        return JsonResponse({"status": "BS.401", "msg": "user auth failed."})
    else:
        if request.method == "POST":
            # b''
            print('request.body: ', request.body)
            print('request.body: ', type(request.body))
            req_dict = json.loads(request.body)
            print('req_json: ', req_dict)
            print('req_json: ', type(req_dict))
            key_flag = req_dict.get('title') and req_dict.get('content') and len(req_dict) == 2
            print('key_flag: ', key_flag)
            # 判断请求体是否正确
            if key_flag:
                title = req_dict['title']
                content = req_dict['content']
                # title返回的是一个list
                title_exist = Article.objects.filter(title=title)
                # 判断是否存在同名的title
                if len(title_exist) != 0:
                    return JsonResponse({"status": "BS.400", "msg": "title already exist, fail to publish."})
                """
                插入数据
                """
                add_art = Article(title=title, content=content, status='alive')
                add_art.save()
                return HttpResponse(add_art)
                return JsonResponse({"status": "BS.200", "msg": "add article success."})
            else:
                return JsonResponse({"status": "BS.400", "message": "please check param."})
        else:
            return HttpResponse("方法错误,你应该使用POST请求方式")
复制代码

2.3 修改文章接口

# 更新文章
def modify_article(request, article_id):
    auth_res = user_auth(request)
    if auth_res == "auth_fail":
        return JsonResponse({"status": "BS.401", "msg": "user auth failed."})
    else:
        if request.method == 'POST':
            modify_req = json.loads(request.body)
            try:
                article = Article.objects.get(id=article_id)
                print("article", article)
                key_flag = modify_req.get('title') and modify_req.get('content') and len(modify_req) == 2
                if key_flag:
                    title = modify_req['title']
                    content = modify_req['content']
                    title_exist = Article.objects.filter(title=title)
                    if len(title_exist) > 1:
                        return JsonResponse({"status": "BS.400", "msg": "title already exist."})

                    # 更新文章
                    old_article = Article.objects.get(id=article_id)
                    old_article.title = title
                    old_article.content = content
                    old_article.save()
                    return JsonResponse({"status": "BS.200", "msg": "modify article sucess."})
            except Article.DoesNotExist:
                return JsonResponse({"status": "BS.300", "msg": "article is not exists,fail to modify."})
        else:
            return HttpResponse("方法错误,你应该使用POST请求方式")
复制代码

2.4 删除文章接口

# 删除文章
def delete_article(request, article_id):
    auth_res = user_auth(request)
    if auth_res == "auth_fail":
        return JsonResponse({"status": "BS.401", "msg": "user auth failed."})
    else:
        if request.method == 'DELETE':
            try:
                article = Article.objects.get(id=article_id)
                article_id = article.id
                article.delete()
                return JsonResponse({"status": "BS.200", "msg": "delete article success."})
            except Article.DoesNotExist:
                return JsonResponse({"status": "BS.300", "msg": "article is not exists,fail to delete."})
        else:
            return HttpResponse("方法错误,你应该使用DELETE请求方式")
复制代码

2.5 token认证

# 用户认证
# 四个简单的接口已经可以运行了,但是在发请求之前没有进行鉴权,毫无安全性可言。下面来实现简单的认证机制。需要用到内建模块hashlib,hashlib提供了常见的摘要算法,如MD5,SHA1等。
def user_auth(request):
    token = request.META.get("HTTP_X_TOKEN", b'')
    print("token: ", token)
    if token:
        # 暂时写上 auth 接口返回的数据
        if token == '0a6db4e59c7fff2b2b94a297e2e5632e':
            return "auth_success"
        else:
            return "auth_fail"
    else:
        return "auth_fail"
复制代码

2.6 接口测试

  在接口开发是不断开发不断测试是一个非常好的习惯。

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File    :   interface_crud_tests.py
@Time    :   2019/9/4 14:22
@Author  :   Crisimple
@Github :    https://crisimple.github.io/
@Contact :   Crisimple@foxmail.com
@License :   (C)Copyright 2017-2019, Micro-Circle
@Desc    :   None
"""
import requests
import unittest


class TestInterfaceCrud(unittest.TestCase):
    @unittest.skip("跳过 test_query_article 测试")
    def test_query_article(self):
        payload = {}
        res = requests.get('http://127.0.0.1:8000/query_article/', params=payload)
        print("test_query_article: ", res.text)

    @unittest.skip("跳过 test_add_article 测试")
    def test_add_article(self):
        payload = {
            "title": "title5",
            "content": "content5",
        }
        Headers = {
            # "Authorization": '通用的token,但是该接口使用的是X-Token',
            "Content-Type": "application/json; charset=utf-8",
            "Accept": "application/json",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3730.400 QQBrowser/10.5.3805.400",
            "X-Token": "0a6db4e59c7fff2b2b94a297e2e5632e"
        }
        res = requests.post('http://127.0.0.1:8000/add_article/', headers=Headers, json=payload)
        print(res.request)
        print(res.text)

    @unittest.skip("跳过 test_modify_article 测试")
    def test_modify_article(self):
        payload = {
            "title": "title1",
            "content": "content1",
        }
        Headers = {
            # "Authorization": '通用的token,但是该接口使用的是X-Token',
            "Content-Type": "application/json; charset=utf-8",
            "Accept": "application/json",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3730.400 QQBrowser/10.5.3805.400",
            "X-Token": "0a6db4e59c7fff2b2b94a297e2e5632e"
        }
        res = requests.post('http://127.0.0.1:8000/modify_article/1', headers=Headers, json=payload)
        print(res.request)
        print(res.text)

    # @unittest.skip("跳过 test_delete_article 测试")
    def test_delete_article(self):
        payload = {
            "title": "title2",
            "content": "content2",
        }
        Headers = {
            # "Authorization": '通用的token,但是该接口使用的是X-Token',
            "Content-Type": "application/json; charset=utf-8",
            "Accept": "application/json",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3730.400 QQBrowser/10.5.3805.400",
            "X-Token": "0a6db4e59c7fff2b2b94a297e2e5632e"
        }
        res = requests.delete('http://127.0.0.1:8000/delete_article/2', headers=Headers, json=payload)
        print(res.request)
        print(res.text)

    @unittest.skip("跳过 test_test_api 测试")
    def test_test_api(self):
        payload = {
            'title': 'title1',
            'content': 'content1',
            'status': 'alive'
        }
        res = requests.post('http://127.0.0.1:8000/test_api/')
        print(res.text)


if __name__ == '__main__':
    unittest.main()
复制代码

五、接口自动化

1. 数据处理

1.1 Excel中数据

  获取 excel 的第几 sheet 页,行数,列数,单元格值,数据写入 excel操作。【GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File    :   operate_excel.py
@Time    :   2019/9/5 10:07
@Author  :   Crisimple
@Github :    https://crisimple.github.io/
@Contact :   Crisimple@foxmail.com
@License :   (C)Copyright 2017-2019, Micro-Circle
@Desc    :   对 Excel 的读写操作
"""
import xlrd
from xlutils.copy import copy


class OperateExcel(object):
    def __init__(self, file_name=None, sheet_id=None):
        """
        :param file_name: excel文件的具体路径名称
        :param sheet_id:  要操作的第几 sheet 页
        """
        if file_name:
            self.file_name = file_name
        else:
            self.file_name = '../data/util_data/operate_excel.xls'

        if sheet_id:
            self.sheet_id = sheet_id
        else:
            self.sheet_id = 0

        self.sheet_table = self.get_sheet()

    # 获取 sheet 页操作对象
    def get_sheet(self):
        data = xlrd.open_workbook(self.file_name)
        sheet_table = data.sheets()[self.sheet_id]
        return sheet_table

    # 获取该 sheet 页的行数和列数,拿到的是一个元组
    def get_sheet_nrows_ncols(self):
        return self.sheet_table.nrows, self.sheet_table.ncols

    # 获取该 sheet 页的行数
    def get_sheet_nrows(self):
        return self.sheet_table.nrows

    # 获取该 sheet 页的列数
    def get_sheet_ncols(self):
        return self.sheet_table.ncols

    # 获取具体单元格的数据
    def get_sheet_cell(self, row, col):
        """
        :param row: 单元格的行值
        :param col: 单元格的列值
        :return: cell_data
        """
        cell_data = self.sheet_table.cell_value(row, col)
        return cell_data

    # 写入数据到 excel 中
    def write_to_excel(self, row, col, value):
        # 同样的先打开 excel 操作句柄
        data = xlrd.open_workbook(self.file_name)
        copy_data = copy(data)
        # 选择写入的 sheet 页
        copy_data_sheet = copy_data.get_sheet(0)
        # 写入数据
        copy_data_sheet.write(row, col, value)
        # 保存数据
        copy_data.save(self.file_name)


if __name__ == "__main__":
    oe = OperateExcel()
    print("获取 excel 表的行数和列表,返回元组形式:", oe.get_sheet_nrows_ncols())
    print("获取 excel 表的行数:", oe.get_sheet_nrows())
    print("获取 excel 表的列数:", oe.get_sheet_ncols())
    print("获取单元格(1, 1)的值:", oe.get_sheet_cell(1, 1))
    print("获取单元格(1, 2)的值:", oe.get_sheet_cell(1, 2))
    print("获取单元格(2, 2)的值:", oe.get_sheet_cell(2, 2))
    oe.write_to_excel(17, 7, '写入的数据')
复制代码

1.2 JSON中数据

GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File    :   operate_json.py
@Time    :   2019/9/5 12:24
@Author  :   Crisimple
@Github :    https://crisimple.github.io/
@Contact :   Crisimple@foxmail.com
@License :   (C)Copyright 2017-2019, Micro-Circle
@Desc    :   操作 JSON 文件中的数据
"""
import json


class OperateJson(object):
    def __init__(self, file_name=None):
        if file_name:
            self.file_name = file_name
        else:
            self.file_name = '../data/util_data/operate_json.json'

        self.data = self.get_json()

    # 读取 json 文件
    def get_json(self):
        with open(self.file_name, encoding='utf-8') as fp:
            data = json.load(fp)
        return data

    # 根据关键词读取数据
    def get_key_data(self, key):
        return self.data[key]


if __name__ == '__main__':
    oj = OperateJson()
    print('login: ', oj.get_key_data("login"))
    print('login.username: ', oj.get_key_data("login")["username"])
    print('login.password: ', oj.get_key_data("login")["username"])
    print('logout: ', oj.get_key_data("logout"))
    print('logout.code: ', oj.get_key_data("logout")["code"])
    print('logout.info: ', oj.get_key_data("logout")["info"])

复制代码
{
  "login": {
    "username": "kevin",
    "password": "121345"
  },
  "logout": {
    "code": 200,
    "info": "logout"
  }
}
复制代码

1.3 数据库中的数据

  数据库用的常用的MySQL。【GitHub示例

  远程连接数据库可能会连接出错的解决方法:GRANT ALL PRIVILEGES ON . TO 'root'@'%' IDENTIFIED BY '你的密码' WITH GRANT OPTION;

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File    :   operate_mysql.py
@Time    :   2019/9/5 16:10
@Author  :   Crisimple
@Github :    https://crisimple.github.io/
@Contact :   Crisimple@foxmail.com
@License :   (C)Copyright 2017-2019, Micro-Circle
@Desc    :   操作 数据库 中的数据
"""

import pymysql
import json


class OperateMysql(object):
    def __init__(self):
        # 数据库初始化连接
        self.connect_interface_testing = pymysql.connect(
            "XXX.XXX.XXX.XXX",
            "XXX",
            "XXXXXXXX",
            "InterfaceTesting",
            cursorclass=pymysql.cursors.DictCursor
        )

        # 创建游标操作数据库
        self.cursor_interface_testing = self.connect_interface_testing.cursor()

    def select_data(self, sql):
        # 执行 sql 语句
        self.cursor_interface_testing.execute(sql)
        # 获取查询到的第一条数据
        first_data = self.cursor_interface_testing.fetchone()
        # 将返回结果转换成 str 数据格式
        first_data = json.dumps(first_data)
        return first_data


if __name__ == "__main__":
    om = OperateMysql()
    res = om.select_data(
        """
            SELECT * FROM test_table;
        """
    )
    print(res)
复制代码

2. 邮件告警

  通常我们做接口自动化测试的时候,自动化用例执行结束后,我们需要首先需要看自动化用例是不是执行结束了,另外它的执行结果是什么。我们不可能一直紧盯着脚本执行,所以当自动化执行结束后,我们需要发送邮件来进行提醒并把自动化的执行情况邮件通知。

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File    :   email_config.py
@Time    :   2019/9/5 18:58
@Author  :   Crisimple
@Github :    https://crisimple.github.io/
@Contact :   Crisimple@foxmail.com
@License :   (C)Copyright 2017-2019, Micro-Circle
@Desc    :   发送邮件配置
"""

import smtplib
from email.mime.text import MIMEText

class EmailConfig(object):

    global send_user
    global mail_host
    global password
    send_user = '发送者邮箱@163.com'
    mail_host = 'smtp.163.com'
    password = '邮箱服务器密码'

    def send_config(self, user_lists, subject, content):
        user = "发件人昵称" + "<" + send_user + ">"
        message = MIMEText(content, _subtype="plain", _charset="utf-8")
        message['Subject'] = subject
        message['From'] = user
        message['To'] = ";".join(user_lists)

        server = smtplib.SMTP()
        server.connect(mail_host)
        server.login(send_user, password)
        server.sendmail(user, user_lists, message.as_string())
        server.close()

    def send_mail(self, pass_cases, fail_cases, not_execute_cases):
        pass_num = float(len(pass_cases))
        fail_num = float(len(fail_cases))
        not_execute_num = float(len(not_execute_cases))

        execute_num = float(pass_num + fail_num)
        total_cases = float(pass_num + fail_num + not_execute_num)
        pass_ratio = "%.2f%%" % (pass_num / total_cases * 100)
        fail_ratio = "%.2f%%" % (fail_num / total_cases * 100)

        user_lists = ['crisimple@foxmail.com']
        subject = "【邮件配置测试】"
        content = "一共 %f 个用例, 执行了 %f 个用例,未执行 %f 个用例;成功 %f 个,通过率为 %s;失败 %f 个,失败率为 %s" % (total_cases, execute_num, not_execute_num, pass_num, pass_ratio, fail_num, fail_ratio)

        self.send_config(user_lists, subject, content)


if __name__ == "__main__":
    ec = EmailConfig()
    ec.send_mail([1, 3, 5], [2, 4, 6], [1, 2, 3])
复制代码

3. 封装测试

3.1 多种请求方式兼容

  通过第四模块的接口开发,我们知道接口的请求方式有多种,在接口测试时我们不可能针对不同请求方式的接口不断的改变它的请求方法形式和参数,所以可以将多种不同请求方式统一整合,只改变请求方法(GET、POST、DELETE、UPDATE)来切换不同的请求形式。【GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File    :   intergrate_request.py
@Time    :   2019/9/6 7:56
@Author  :   Crisimple
@Github :    https://crisimple.github.io/
@Contact :   Crisimple@foxmail.com
@License :   (C)Copyright 2017-2019, Micro-Circle
@Desc    :   多种请求方法集成
"""

import requests
import json


class IntergrateRequest():
    # 请求 request方法
    def get_req(self, url, data=None, headers=None):
        if headers is not None:
            res = requests.get(url, data, headers).json()
        else:
            res = requests.get(url,  data).json()
        return res

    # post 请求方式
    def post_req(self, url, data=None, headers=None):
        if headers is not None:
            res = requests.post(url, data, headers).json()
        else:
            res = requests.post(url,  data).json()
        return res

    # delete 请求方式
    def delete_req(self, url, data=None, headers=None):
        if headers is not None:
            res = requests.delete(url, data, headers).json()
        else:
            res = requests.delete(url,  data).json()
        return res

    def main_req(self, method, url, data=None, headers=None):
        if method == "get":
            res = self.get_req(url, data, headers)
        elif method == 'post':
            res = self.post_req(url, data, headers)
        elif method == 'delete':
            res = self.delete_req(url, data, headers)
        else:
            res = "你的请求方式暂未开放,请耐心等待"
        return json.dumps(res, ensure_ascii=False, indent=4, sort_keys=True)


if __name__ == "__main__":
    ir = IntergrateRequest()
    method = 'get'
    url = 'http://127.0.0.1:8000/query_article/'
    data = None
    headers = None
    print(ir.main_req(method, url, data, headers))
复制代码

3.2 自动化封装

  前面已经把相当一部分的准备工作做好了,接下来就该进行对各个模块进行封装。

(1) 获取试用例关键字段

  等一下详细说明:【GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File    :   testcases_keyword.py
@Time    :   2019/9/6 16:21
@Author  :   Crisimple
@Github :    https://crisimple.github.io/
@Contact :   Crisimple@foxmail.com
@License :   (C)Copyright 2017-2019, Micro-Circle
@Desc    :   None
"""

class TestcasesKeyword(object):
    CASE_ID = '0'
    CASE_NAME = '1'
    IS_EXECUTE = '2'
    INTERFACE_URL = '3'
    METHOD = '4'
    HEADER = '5'
    PAYLOAD = '6'
    EXPECTED_RESULT = '7'
    ACTUAL_RESULT = '8'


# 获取自动化用例 ID
def get_case_id():
    return TestcasesKeyword.CASE_ID

def get_case_name():
    return TestcasesKeyword.CASE_NAME

def get_is_execute():
    return TestcasesKeyword.IS_EXECUTE

def get_interface_url():
    return TestcasesKeyword.INTERFACE_URL

def get_method():
    return TestcasesKeyword.METHOD

def get_header():
    return TestcasesKeyword.HEADER

def get_payload():
    return TestcasesKeyword.PAYLOAD

def get_expected_result():
    return TestcasesKeyword.EXPECTED_RESULT

def get_actual_result():
    return TestcasesKeyword.ACTUAL_RESULT


if __name__ == "__main__":
    print(get_case_id())
复制代码

(2) 业务场景封装

  写代码的作用就是为业务场景服务,是的前面各个模块只是我们的技术栈的积累。这里开始我们算是真正进入业务层面逻辑的设计。比如对于接口自动化这块的测试,拿到自动化用例,我们怎么处理这些用例呢?如果自动化用例是存放在 Excel 中的话,我们首选要拿到每条测试用例各个关键的字段值,根据这些关键字的特定含义看是否执行,是否给接口传header,或是将用例的最后执行结果写回到 execel 中去。是的没错,通过这样的描述我们就是在对自动化用例做业务层面的具体封装。【GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File    :   get_excel_testcases.py
@Time    :   2019/9/6 18:14
@Author  :   Crisimple
@Github :    https://crisimple.github.io/
@Contact :   Crisimple@foxmail.com
@License :   (C)Copyright 2017-2019, Micro-Circle
@Desc    :   None
"""

from util.operate_excel import OperateExcel
from basic import testcases_keyword


class GetExcelTestcases(object):
    def __init__(self):
        self.oe = OperateExcel()

    # 获取测试用例条数,也就是 Excel 中的行数
    def get_cases_num(self):
        return self.oe.get_sheet_nrows()

    # 判断是否携带 headers
    def is_header(self, row):
        col = int(testcases_keyword.get_header())
        header = self.oe.get_sheet_cell(row, col)
        if header is not None:
            return header
        else:
            print("你的 header 呢?")
            return None

    # 判断该条用例是否执行
    def get_is_run(self, row):
        flag = None
        col = int(testcases_keyword.get_is_execute())
        is_run = self.oe.get_sheet_cell(row, col)
        if is_run is not None:
            flag = True
        else:
            flag = False
        return flag

    # 获取不同接口的请求方式
    def get_method(self, row):
        col = int(testcases_keyword.get_method())
        method = self.oe.get_sheet_cell(row, col)
        return method

    # 获取要测试的接口链接
    def get_url(self, row):
        col = int(testcases_keyword.get_interface_url())
        url = self.oe.get_sheet_cell(row, col)
        return url

    # 获取接口参数
    def get_payload(self, row):
        col = int(testcases_keyword.get_payload())
        payload = self.oe.get_sheet_cell(row, col)
        if payload is None:
            return None
        return payload

    # 获取预期结果
    def get_expected_result(self, row):
        col = int(testcases_keyword.get_expected_result())
        expected_result = self.oe.get_sheet_cell(row, col)
        if expected_result is None:
            return None
        return expected_result

    # 写入实际结果
    def write_actual_result(self, row, value):
        col = int(testcases_keyword.get_actual_result())
        self.oe.write_to_excel(row, col, value)


if __name__ == "__main__":
    gety = GetExcelTestcases()
    print(gety.get_cases_num())
    print(gety.is_header(1))
复制代码

3. 执行自动化用例

  接下来就是执行测试用例了。【GitHub示例

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File    :   run_excel_testcases.py
@Time    :   2019/9/7 13:05
@Author  :   Crisimple
@Github :    https://crisimple.github.io/
@Contact :   Crisimple@foxmail.com
@License :   (C)Copyright 2017-2019, Micro-Circle
@Desc    :   None
"""

from basic.get_excel_testcases import GetExcelTestcases
from basic.intergrate_request import IntergrateRequest
from util.email_config import EmailConfig
from util.operate_json import OperateJson
from util.compare_str import CompareStr


class RunExcelTestcases(object):
    def __init__(self):
        self.gtc = GetExcelTestcases()
        self.ir = IntergrateRequest()
        self.ec = EmailConfig()
        self.oj = OperateJson()
        self.cs = CompareStr()

    # 执行测试用例
    def run_testcases(self):
        # 定义空列表,存放执行成功和失败的测试用例
        pass_lists = []
        fail_lists = []
        no_execute_lists = []
        # no_execute_case_name = []

        # 获取总的用例条数
        cases_num = self.gtc.get_cases_num()
        # 遍历执行每一条测试用例
        for case in range(1, cases_num):
            # 用例是否执行
            is_run = self.gtc.get_is_run(case)
            # print("is_run: ", is_run)
            # 接口的请求方式
            method = self.gtc.get_method(case)
            # 请求测试接口
            url = self.gtc.get_url(case)
            # 要请求的数据
            data = self.gtc.get_payload(case)
            # 取出 header
            if case == 1:
                header = None
            else:
                header = self.oj.get_json()
            # 获取预期结果值 expected_result
            expected_result = self.gtc.get_expected_result(case)
            if is_run is True:
                res = self.ir.main_req(method, url, data, header)
                if self.cs.is_contain(expected_result, res):
                    self.gtc.write_actual_result(case, 'pass')
                    pass_lists.append(case)
                else:
                    self.gtc.write_actual_result(case, res)
                    fail_lists.append(case)
            else:
                no_execute_lists.append(case)
        print("没有执行的测试用例有, 按序号有:", no_execute_lists)
        self.ec.send_mail(pass_lists, fail_lists, no_execute_lists)


if __name__ == "__main__":
    rts = RunExcelTestcases()
    rts.run_testcases()
复制代码

4. 持续集成

  为什么要使用持续继承环境呢?通过前面的开发测试整个流程,我们清晰的发现,不管是接口还是自动化程序执行,都需要人为来控制,这是个很低技术含量但是又是不得不做的一个事。引进持续继承,就是让它来做一些重复的事情。

4.1 Jenkins环境搭建

(1) 环境配置

# jenkins是基于 Java 环境的,所以首先安装Java SDK
sudo apt-get install openjdk-8-jdk

# 将 jenkins 存储库密钥添加到系统
wget -q -O - https://pkg.jenkins.io/debian/jenkins-ci.org.key | sudo apt-key add -

# 将Debian包存储库地址附加到服务器的sources.list
echo deb http://pkg.jenkins.io/debian-stable binary/ | sudo tee /etc/apt/sources.list.d/jenkins.list

# 更新存储库
sudo apt-get update

# 安装 Jenkins
sudo apt-get install jenkins

复制代码

(2) Jenkins 的常用命令

# Jenkins 启动 | 查看状态 | 重启 | 停止
sudo service jenkins start|status|restart|stop

# jenkins启动后的访问地址:http:// ip_address_or_domain_name :8080

# 访问上面的地址会发现需要输入初始密码,查看获取初始密码
sudo cat /var/lib/jenkins/secrets/initialAdminPassword
复制代码

4.2 接口继承

(1) 新建模拟接口(InterfaceTestingMock)任务

 

(2) 源码管理

 

 

(3) 配置构建脚本

  构建的是否,你可能会出现一些问题。我遇到就是无法创建超级用户,解决方案是:是权限的问题,我的解决方案是sudo python manage.py createsuperuser,执行创建超级用户前加sudo就可以了。如有问题可以留言

 

4.3 接口自动化集成

对于在命令行中执行程序时,通常会报错NoMoudle的错误的 解决方案。


绵薄之力【软件测试全套资源分享】

最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走

这些资料,对于从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!凡事要趁早,特别是技术行业,一定要提升技术功底。希望对大家有所帮助……基础知识、Linux必备、Shell、互联网程序原理、Mysql数据库、抓包工具专题、接口测试工具、测试进阶-Python编程、Web自动化测试、APP自动化测试、接口自动化测试、测试高级持续集成、测试架构开发测试框架、性能测试、安全测试等配套学习资源免费分享~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/343632.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

设置软件以管理员权限开机自启动,MemReduct为例

目录 一.MemReduct 1.简介 2.下载方式 二.问题描述 三.具体操作 1.在windows搜索框搜索"任务计划程序" 并打开 2.在右方操作栏点击创建任务 3.常规选项 4.操作选项 5.触发器选项 6.条件选项 7.启动 一.MemReduct 1.简介 支持中文,是一个很方便的内存清理软…

pyTorch下载和cuda下载以及学习笔记

pytorch官方网站&#xff0c;cuda官方网站 CUDA下载&#xff1a;https://developer.nvidia.com/cuda-toolkit-archive CUDNN下载&#xff1a;https://developer.nvidia.com/rdp/cudnn-download pytorch下载&#xff1a;pytorch.org 任务管理器中只显示CUDA占用的专用内存&#…

postman实现接口测试详细教程

各位小伙伴大家好, 今天为大家带来postman实战接口测试详细教程 一、通过接口文档集合抓包分析接口 通过fiddler抓包获取到注册接口URL地址及相关参数数据,并通过接口文档分析接口参数内容及参数说明, 如有必要的依赖条件必须进行梳理, 如token等 Fiddler抓包注册接口请求与…

领英精灵和领英助理哪个好?为什么领英精灵是LinkedIn最好的配套工具?

领英精灵和领英助理哪个好&#xff1f;这是很多使用领英工具的人会问的问题&#xff0c;这2个工具我都有使用过&#xff0c;下面说说我使用后的感受和区别&#xff1a; 公司背景 首先从他们的公司背景看&#xff0c;领英精灵是汇聚了国内领英专家针对领英规则设计的&#xff…

32、基于51单片机红外智能垃圾桶系统设计

摘要 随着现代化进程的日益推进&#xff0c;科技越来越发达&#xff0c;人们的生活水平也提高了&#xff0c;城市化程度越来越高&#xff0c;与此同时也带了许多问题&#xff0c;生活垃圾越来越多垃圾设施却不够完善。无论是在公共场合还是家庭厨房的垃圾大都是没有盖或者有盖…

C语言——指针(进阶详解)

文章目录指针概念的回顾1.字符指针1.1字符指针练习题2.指针数组3.数组指针3.1数组指针的定义3.2 &数组名和数组名的区别3.3数组指针的使用3.4一组简单的练习题4.数组和指针作为函数参数4.1一维数组传参**总结**4.2二维数组传参**总结**4.3一级指针传参**总结**4.4二级指针传…

Spring Boot RCE到内存马探索

前言 SpringBootVulExploit是Spring Boot漏洞Check list&#xff0c;但在真正的环境中进行漏洞利用还是有一段距离的&#xff0c;因此衍生出了SpringBootExploit工具。本文是对该Check list到内存马探索之路的记录。再此过程中学到了很多知识&#xff0c;收获了很多&#xff0…

线性双功能PEG羧酸Acetic Acid-PEG-Acetic Acid,AA-PEG-AA,羧酸PEG羧酸

产品名称&#xff1a; 1、英文&#xff1a;Acetic Acid-PEG-Acetic Acid AA-PEG-AA 2、中文&#xff1a;羧酸-聚乙二醇-羧酸 产品介绍&#xff1a; AA-PEG-AA是一种线性双功能PEG羧酸试剂。PEG和COOH基团之间存在亚甲基&#xff08;CH2&#xff09;键。AA-PEG-AA也称为CM-P…

测试平台系列——编写oss管理页面

上一节我们编写好了oss相关的crud接口&#xff0c;那这一节我们就得为oss数据的管理编写一个新的页面了。 即将做的是一个极度精简的文件管理页面。 效果图 因为我每次都是写完一段代码&#xff0c;然后编写对应教程&#xff0c;所以效果图这种东西自然是不在话下: 图片可以…

更改SAP GUI登录界面信息

在SAP GUI的登录界面&#xff0c;左部输入登录信息如客户端、用户名、密码等&#xff0c;右部空余部分可维护一些登录信息文本&#xff0c;如登录的产品、客户端说明及注意事项等&#xff0c;此项操作详见SAP Notes 205487 – Own text on SAPGui logon screen 维护文档使用的…

从春节后央行的首批罚单,看金融反欺诈反洗钱的复杂性

目录 个人信息保护的问题 征信管理的问题 反洗钱与反欺诈的问题 金融欺诈愈加复杂多变 金融机构如何增强反欺诈反洗钱 春节后&#xff0c;央行公示首批罚单。其中&#xff0c;厦门银行被中国人民银行福州中心支行给予警告&#xff0c;并没收违法所得767.17元&#xff0c;处…

30个HTML+CSS前端开发案例(五)

30个HTMLCSS前端开发案例&#xff08;21-25&#xff09;本人说明全屏加载动画效果代码实现效果吃豆豆动画效果代码实现效果鼠标悬停3D翻转效果代码实现效果3D旋转木马效果代码实现效果flex弹性布局-酷狗音乐播放列表代码实现效果资源包本人说明 本专栏为记录博主的毕业设计而开…

论文阅读 | Restormer: Efficient Transformer for High-Resolution Image Restoration

前言&#xff1a;CVPR2022oral 用transformer应用到low-level任务 Restormer: Efficient Transformer for High-Resolution Image Restoration 引言 low-level task 如deblurring\denoising\dehazing等任务多是基于CNN做的&#xff0c;这样的局限性有二&#xff1a; 第一是卷…

文献综述,参考文献引用

知网完成文献综述 点击已选&#xff1a; 点导出题录 点自定义&#xff0c;点摘要&#xff0c;点预览 复制到剪切板 参考文献如何在论文中标注引用 国内外研究综述 简单介绍主题和所用理论&#xff0c;用第一段写综述的总结。 【 ...作...在西方国家已经有...多年的历史&…

基于应力的拓扑优化的高效3D灵敏度分析代码(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【FFMPEG源码分析】从ffplay源码摸清ffmpeg框架(三)

从ffplay源码分析这篇文章中可以知道int stream_component_open(VideoState *is, int stream_index)函数是创建和初始化decoder的入口。本篇文章从此入口看下ffmpeg中decoder的内部结构是什么样子。 同样先提出几个问题&#xff0c;带着问题梳理源码&#xff0c;效率贼快啊 ff…

Linux(Linux的连接使用)

连接Linux我们一般使用CRT或者Xshell工具进行连接使用。 如CRT使用SSH的方式 输出主机&#xff0c;账户&#xff0c;密码那些就可以连接上了。 Linux系统是一个文件型操作系统&#xff0c;有一句话说Linux的一切皆是文件。Linux系统的启动大致有下面几个步骤 Linux系统有7个运…

excel应用技巧:如何用函数制作简易抽奖动图

利用INDEX函数和随机整数函数RANDBETWEEN配合&#xff0c;在Excel中做一个简单的抽奖器&#xff0c;可以随机抽取姓名或者奖品。有兴趣的伙伴可以做出来试试&#xff0c;撞撞2023年好运气。每次年会大家最期待的就是抽奖环节。为了看看自己今年运气怎么样&#xff0c;会不会获奖…

中国版ChatGPT来了! 如何解读ChatGPT将带来的技术变革

最近这段时间&#xff0c;ChatGPT真的是太火了&#xff01;各平台都在铺天盖地式的宣传&#xff0c;相信在这么些天的宣传中&#xff0c;大家也对ChatGPT有了一个大概的了解&#xff0c;我们这边也就简单介绍一下。据ChatGPT自我介绍&#xff0c;它是一款预训练语言模型&#x…

ContextCapture Master 倾斜摄影测量实景三维建模技术

ContextCapture实景建模大师是一套无需人工干预&#xff0c;通过影像自动生成高分辨率的三维模型的软件解决方案。它集合了全球最先进数字影像处理、计算机虚拟现实以及计算机几何图形算法&#xff0c;在易用性、数据兼容性、运算性能、友好的人机交互及自由的硬件配置兼容性等…