Pytest测试实战

news2025/1/19 17:17:37

Pytest测试框架是动态语言Python专用的测试框架,使用起来非常的简单,这主要得易于它的设计,Pytest测试框架具备强大的功能,丰富的第三方插件,以及可扩展性好,可以很好的和unittest测试框架能够结合起来在项目中使用。本文章主要介绍Pytest测试框架中参数化的详细信息。

      参数化的本质是对列表中的对象进行循环,然后把循环的对象进行一一的赋值,它的应用场景主要是基于相同的业务场景,但是需要不同的测试数据来测试从而达到最大化的覆盖更多的业务场景和测试的覆盖率。理解了这样的一个思想之后,我们就以两个数想加作为案例,来演示Pytest测试框架的参数化实际应用,另外一点需要特别说的是在Pytest测试框架中参数化使用的方式是通过装饰器的方式来进行。刚才也说到它的本质是对列表中的对象进行循环和赋值,那么这个对象可以是列表,也可以是元组以及和字典数据类型,见如下的实战案例,把测试的数据分离到不同的对象中(列表,元组,字典),源码如下:

#!/usr/bin/env python
#!coding:utf-8
import pytest


def add(a,b):
return a+b


@pytest.mark.parametrize('a,b,expect',[
[1,1,2],
[2,2,4],
[3,3,6],
[4,4,8],
[5,5,10]
])
def test_add_list(a,b,expect):
assert add(a,b)==expect

@pytest.mark.parametrize('a,b,expect',[
(1,1,2),
(2,2,4),
(3,3,6),
(4,4,8),
(5,5,10)
])
def test_add_tuple(a,b,expect):
assert add(a,b)==expect


@pytest.mark.parametrize('data',[
{'a':1,'b':1,'expect':2},
{'a':5,'b':5,'expect':10}
])
def test_add_dict(data):
assert add(data['a'],data['b'])==data['expect']

if __name__ == '__main__':
pytest.main(["-s","-v","test_one.py"])

执行后的结果信息如下:

在如上的结果信息中,可以看到真正实现测试用例的代码是很少的,而且把参数化使用到的数据分离到不同的数据类型中。

      下面结合API的测试场景来考虑,被测试的API的代码如下:

#!/usr/bin/env python
#!coding:utf-8
from flask import Flask,jsonify
from flask_restful import Api,Resource,reqparse


app=Flask(__name__)
api=Api(app)


class LoginView(Resource):
def get(self):
return {'status':0,'msg':'ok','data':'this is a login page'}

def post(self):
parser=reqparse.RequestParser()
parser.add_argument('username', type=str, required=True, help='用户名不能为空')
parser.add_argument('password',type=str,required=True,help='账户密码不能为空')
parser.add_argument('age',type=int,help='年龄必须为正正数')
parser.add_argument('sex',type=str,help='性别只能是男或者女',choices=['女','男'])
args=parser.parse_args()
return jsonify(args)

api.add_resource(LoginView,'/login',endpoint='login')

if __name__ == '__main__':
app.run(debug=True)

在基于API测试维度的思想,针对该接口测试我们不考虑接口的安全性,高并发以及它的稳定性方面,单纯的只是从功能层面来考虑进行测试,那么需要针对每个参数是否缺少都得需要进行验证,就会涉及到五个测试用例的设计,我们把数据分别分离到主流的文件中,文件的格式主要为JSON,Yaml,Excel和CSV的文件,先来看分离到JSON的文件内容:

{
  "item":
  [
    {
      "request":
      {
        "url": "http://localhost:5000/login",
        "body":
        {
          "password":"admin",
          "sex":"男",
          "age":18
        }
      },
      "response":
      [
        {
          "message":
          {
            "username": "用户名不能为空"
          }
        }
      ]
    },
    {
      "request":
      {
        "url": "http://localhost:5000/login",
        "body":
        {
          "username":"wuya",
          "sex":"男",
          "age":18
        }
      },
      "response":
      [
        {
        "message":
        {
          "password": "账户密码不能为空"
        }
      }
      ]
    },
    {
      "request":
      {
        "url": "http://localhost:5000/login",
        "body":
        {
          "username":"wuya",
          "password":"admin",
          "sex":"asdf",
          "age":18
        }
      },
      "response":
      [
        {
          "message":
          {
            "sex": "性别只能是男或者女"
          }
        }
      ]
    },
    {
      "request":
      {
        "url": "http://localhost:5000/login",
        "body":
        {
          "username":"wuya",
          "password":"admin",
          "sex":"男",
          "age":"rrest"
        }
      },
      "response":
      [
        {
          "message":
          {
            "age": "年龄必须为正正数"
          }
        }
      ]
    },
    {
      "request":
      {
        "url": "http://localhost:5000/login",
        "body":
        {
          "username":"wuya",
          "password":"admin",
          "sex":"男",
          "age":"18"
        }
      },
      "response":
      [
        {
          "age": 18,
          "password": "admin",
          "sex": "男",
          "username": "wuya"
        }
      ]
    }
  ]
}

涉及到的测试代码为:

#!/usr/bin/env python
#!coding:utf-8
import  pytest
import  requests
import  json


def readJson():
   return json.load(open('login.json','r'))['item']

@pytest.mark.parametrize('data',readJson())
def test_json_login(data):
   r=requests.post(
      url=data['request']['url'],
      json=data['request']['body'])
   assert r.json()==data['response'][0]


if __name__ == '__main__':
   pytest.main(["-s","-v","test_json_login.py"])

再来看分离到Yaml文件的数据:

---
#用户名请求为空
"url": "http://localhost:5000/login"
"body": '{
           "password":"admin",
           "sex":"男",
           "age":18
        }'
"expect": '{
              "message": {
                  "username": "用户名不能为空"
              }
          }'
---
#密码参数为空
"url": "http://localhost:5000/login"
"body": '{
           "username":"admin",
           "sex":"男",
           "age":18
        }'
"expect": '{
              "message": {
                  "password": "账户密码不能为空"
              }
          }'
---
#校验性别参数的验证
"url": "http://localhost:5000/login"
"body": '{
           "username":"wuya",
           "password":"admin",
           "sex":"asdf",
           "age":18
         }'
expect: '{
             "message": {
                 "sex": "性别只能是男或者女"
             }
         }'
---
#校验年龄是否是正整数
"url": "http://localhost:5000/login"
"body": '{
           "username":"wuya",
           "password":"admin",
           "sex":"男",
           "age":"rrest"
         }'
"expect": '{
               "message": {
                   "age": "年龄必须为正正数"
               }
           }'
---
#登录成功
"url": "http://localhost:5000/login"
"body": '{
           "username":"wuya",
           "password":"admin",
           "sex":"男",
           "age":"18"
         }'
"expect": '{
               "age": 18,
               "password": "admin",
               "sex": "男",
               "username": "wuya"
           }'

涉及到的测试代码为:

#!/usr/bin/env python
#!coding:utf-8
import  pytest
import  requests
import  yaml


def readYaml():
   with open('login.yaml','r') as f:
      return list(yaml.safe_load_all(f))


@pytest.mark.parametrize('data',readYaml())
def test_login(data):
   r=requests.post(
      url=data['url'],
      json=json.loads(data['body']))
   assert r.json()==json.loads(data['expect'])

分离到CSV的文件内容为:

涉及到的测试代码为:

#!/usr/bin/env python
#!coding:utf-8
import  pytest
import  requests
import  csv


def readCsv():
   data=list()
   with open('login.csv','r') as f:
      reader=csv.reader(f)
      next(reader)
      for item in reader:
         data.append(item)
   return data



@pytest.mark.parametrize('data',readCsv())
def test_csv_login(data):
   r=requests.post(
      url=data[0],
      json=json.loads(data[1]))
   assert r.json()==json.loads(data[2])

最后来看分离到Excel的文件内容:

涉及到的测试代码为:

#!/usr/bin/env python
#!coding:utf-8
import  pytest
import  requests
import  xlrd


def readExcel():
   data=list()
   book=xlrd.open_workbook('login.xls')
   sheet=book.sheet_by_index(0)
   for item in range(1,sheet.nrows):
      data.append(sheet.row_values(item))
   return data



@pytest.mark.parametrize('data',readExcel())
def test_excel_login(data):
   r=requests.post(
      url=data[0],
      json=json.loads(data[1]))
   assert r.json()==json.loads(data[2])
 

其实我们发现套路都是一样的,不管把数据分离到什么样的数据格式下,都得符合它的本质思想,也就是参数化的本质是对列表中的对象进行循环赋值,把握住这样的一个思想就可以了。整合上面的所有代码,完整代码为:

#!/usr/bin/env python
#!coding:utf-8
import  pytest
import  requests
import  json
import  yaml
import  csv
import  xlrd




def readJson():
   return json.load(open('login.json','r'))['item']

def readYaml():
   with open('login.yaml','r') as f:
      return list(yaml.safe_load_all(f))

def readCsv():
   data=list()
   with open('login.csv','r') as f:
      reader=csv.reader(f)
      next(reader)
      for item in reader:
         data.append(item)
   return data


def readExcel():
   data=list()
   book=xlrd.open_workbook('login.xls')
   sheet=book.sheet_by_index(0)
   for item in range(1,sheet.nrows):
      data.append(sheet.row_values(item))
   return data


@pytest.mark.parametrize('data',readJson())
def test_json_login(data):
   r=requests.post(
      url=data['request']['url'],
      json=data['request']['body'])
   assert r.json()==data['response'][0]


@pytest.mark.parametrize('data',readYaml())
def test_yaml_login(data):
   r=requests.post(
      url=data['url'],
      json=json.loads(data['body']))
   assert r.json()==json.loads(data['expect'])

@pytest.mark.parametrize('data',readCsv())
def test_csv_login(data):
   r=requests.post(
      url=data[0],
      json=json.loads(data[1]))
   assert r.json()==json.loads(data[2])


@pytest.mark.parametrize('data',readExcel())
def test_excel_login(data):
   r=requests.post(
      url=data[0],
      json=json.loads(data[1]))
   assert r.json()==json.loads(data[2])

执行后的结果信息为:

     

      Pytest测试框架最强大的功能除了丰富的第三方插件外,还有就是它的Fixture和共享Fixture的conftest.py,下面具体来看被测试的接口代码:

from flask import  Flask,make_response,jsonify,abort,request
from flask_restful import  Api,Resource
from flask_httpauth import  HTTPBasicAuth

from flask import Flask
from flask_jwt import JWT, jwt_required, current_identity
from werkzeug.security import safe_str_cmp

app=Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = 'super-secret'
api=Api(app=app)
auth=HTTPBasicAuth()

@auth.get_password
def get_password(name):
   if name=='admin':
      return 'admin'
@auth.error_handler
def authoorized():
   return make_response(jsonify({'msg':"请认证"}),403)

books=[
   {'id':1,'author':'wuya','name':'Python接口自动化测试实战','done':True},
   {'id':2,'author':'无涯','name':'Selenium3自动化测试实战','done':False}
]


class User(object):
   def __init__(self, id, username, password):
      self.id = id
      self.username = username
      self.password = password

   def __str__(self):
      return "User(id='%s')" % self.id

users = [
   User(1, 'wuya', 'asd888'),
   User(2, 'admin', 'admin'),
]

username_table = {u.username: u for u in users}
userid_table = {u.id: u for u in users}

def authenticate(username, password):
   user = username_table.get(username, None)
   if user and safe_str_cmp(user.password.encode('utf-8'), password.encode('utf-8')):
      return user

def identity(payload):
   user_id = payload['identity']
   return userid_table.get(user_id, None)

jwt = JWT(app, authenticate, identity)

class Books(Resource):
   # decorators = [auth.login_required]
   decorators=[jwt_required()]

   def get(self):
      return jsonify({'status':0,'msg':'ok','datas':books})

   def post(self):
      if not request.json:
         return jsonify({'status':1001,'msg':'请求参数不是JSON的数据,请检查,谢谢!'})
      else:
         book = {
            'id': books[-1]['id'] + 1,
            'author': request.json.get('author'),
            'name': request.json.get('name'),
            'done': True
         }
         books.append(book)
         return jsonify({'status':1002,'msg': '添加书籍成功','datas':book}, 201)


class Book(Resource):
   # decorators = [auth.login_required]
   # decorators = [jwt_required()]
   def get(self,book_id):
      book = list(filter(lambda t: t['id'] == book_id, books))
      if len(book) == 0:
         return jsonify({'status': 1003, 'msg': '很抱歉,您查询的书的信息不存在'})
      else:
         return jsonify({'status': 0, 'msg': 'ok', 'datas': book})

   def put(self,book_id):
      book = list(filter(lambda t: t['id'] == book_id, books))
      if len(book) == 0:
         return jsonify({'status': 1003, 'msg': '很抱歉,您查询的书的信息不存在'})
      elif not request.json:
         return jsonify({'status': 1001, 'msg': '请求参数不是JSON的数据,请检查,谢谢!'})
      elif 'author' not in request.json:
         return jsonify({'status': 1004, 'msg': '请求参数author不能为空'})
      elif 'name' not in request.json:
         return jsonify({'status': 1005, 'msg': '请求参数name不能为空'})
      elif 'done' not in request.json:
         return jsonify({'status': 1006, 'msg': '请求参数done不能为空'})
      elif type(request.json['done'])!=bool:
         return jsonify({'status': 1007, 'msg': '请求参数done为bool类型'})
      else:
         book[0]['author'] = request.json.get('author', book[0]['author'])
         book[0]['name'] = request.json.get('name', book[0]['name'])
         book[0]['done'] = request.json.get('done', book[0]['done'])
         return jsonify({'status': 1008, 'msg': '更新书的信息成功', 'datas': book})

   def delete(self,book_id):
      book = list(filter(lambda t: t['id'] == book_id, books))
      if len(book) == 0:
         return jsonify({'status': 1003, 'msg': '很抱歉,您查询的书的信息不存在'})
      else:
         books.remove(book[0])
         return jsonify({'status': 1009, 'msg': '删除书籍成功'})

api.add_resource(Books,'/v1/api/books')
api.add_resource(Book,'/v1/api/book/<int:book_id>')

if __name__ == '__main__':
   app.run(debug=True)

我们通过token的方式,首先需要授权,授权成功后才可以针对书籍这些接口进行操作,如添加删除以及查看所有的书籍信息,那么获取token这部分的代码完全可以放在conftest.py里面,具体源码为:

#!/usr/bin/env python
#!coding:utf-8
import  requests
import  pytest

@pytest.fixture()
def getToken():
   '''获取token'''
   r=requests.post(
      url='http://localhost:5000/auth',
      json={"username":"wuya","password":"asd888"})
   return r.json()['access_token']

Fixture一点需要考虑的是初始化与清理,也就是说在一个完整的测试用例中,都必须都得有初始化与清理的部分,这样才是一个完整的测试用例的。Fixture可以很轻松的来解决这部分,还有一点需要说的是Fixture的函数也可以和返回值整合起来,如添加书籍成功后,把数据ID返回来,下面就以查看书籍为案例,那么查看书籍前提是需要添加书籍,这样可以查看,最后把添加的书籍删除,这样一个测试用例执行完成后才符合它的完整流程,具体测试代码如下:

#!/usr/bin/env python
#!coding:utf-8
import pytest
import  requests

def writeBookID(bookID):
   with open('bookID','w') as f:
      f.write(str(bookID))

@pytest.fixture()
def getBookID():
   with open('bookID','r') as f:
      return  f.read()

def addBook(getToken):
   r=requests.post(
      url='http://localhost:5000/v1/api/books',
      json={"author": "wuya","done": False,"name": "Selenium3自动化测试实战"},
      headers={'Authorization':'JWT {0}'.format(getToken)})
   print('添加书籍:\n',r.json())
   writeBookID(r.json()[0]['datas']['id'])


def delBook(getToken,getBookID):
   r=requests.delete(
      url='http://localhost:5000/v1/api/book/{0}'.format(getBookID),
      headers={'Authorization':'JWT {0}'.format(getToken)})
   print('删除书籍:\n',r.json())

@pytest.fixture()
def init(getToken,getBookID):
   addBook(getToken)
   yield
   delBook(getToken,getBookID)

def test_get_book(init,getToken,getBookID):
   r=requests.get(
      url='http://localhost:5000/v1/api/book/{0}'.format(getBookID),
      headers={'Authorization':'JWT {0}'.format(getToken)})
   print('查看书籍:\n',r.json())

在如上的代码中可以看到,我们刻意了写了init的Fixture函数,就是使用了它的初始化与清理的思想,当然还可以结合内置的Fixture把代码改造为如下的部分:

#!/usr/bin/env python
#!coding:utf-8
import pytest
import  requests
import  allure


@allure.step
def addBook(getToken):
   r=requests.post(
      url='http://localhost:5000/v1/api/books',
      json={"author": "无涯","done": False,"name": "Selenium3自动化测试实战"},
      headers={'Authorization':'JWT {0}'.format(getToken)})
   print('添加书籍:\n',r.json())
   return r.json()[0]['datas']['id']


@allure.step
def delBook(getToken,bookID):
   r=requests.delete(
      url='http://localhost:5000/v1/api/book/{0}'.format(bookID),
      headers={'Authorization':'JWT {0}'.format(getToken)})
   print('删除书籍:\n',r.json())


def test_get_book(getToken,tmpdir):
   f=tmpdir.join('bookid.txt')
   f.write(addBook(getToken))
   r=requests.get(
      url='http://localhost:5000/v1/api/book/{0}'.format(f.read()),
      headers={'Authorization':'JWT {0}'.format(getToken)})
   delBook(getToken=getToken,bookID=f.read())
   print('查看书籍:\n',r.json())

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

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

相关文章

手机如何下载短视频到本地:成都鼎茂宏升文化传媒公

手机如何下载短视频到本地 ​随着移动互联网的迅猛发展&#xff0c;短视频已经成为人们生活中不可或缺的一部分。从娱乐、学习到社交&#xff0c;短视频以其短小精悍、内容丰富的特点&#xff0c;吸引了大量用户的关注。然而&#xff0c;有时我们可能希望将喜欢的短视频保存到…

计算机网络-DHCPv6基础

前面我们学习了IPv6地址可以通过手动配置、无状态自动配置、DHCPv6配置&#xff0c;这里简单学习下DHCPv6的知识点。 一、DHCPv6概述 DHCPv6 (Dynamic Host Configuration Protocol for IPv6) 是一种网络协议&#xff0c;设计用于IPv6网络环境中自动为网络设备分配必要的配置信…

LiveGBS流媒体平台GB/T28181用户手册-服务器概览:通道信息、负载信息、CPU使用、存储使用、带宽使用(Mbps)、内存使用

LiveGBS用户手册-服务器概览&#xff1a;通道信息、负载信息、CPU使用、存储使用、带宽使用&#xff08;Mbps&#xff09;、内存使用 1、服务器概览1.1、通道信息1.2、负载信息1.2.1、信息说明1.2.2、会话列表 1.3、CPU使用1.4、存储使用1.5、带宽使用&#xff08;Mbps&#xf…

理解 Python 中的 `super()` 与 `__init__()` 方法

在 Python 的面向对象编程中&#xff0c;super() 函数和 __init__() 方法是两个非常重要的概念。它们在类的继承和初始化过程中扮演着关键的角色。本文将深入探讨这两个概念的工作原理&#xff0c;并通过示例代码来展示它们的使用。 基本原理 __init__() 方法 __init__() 是…

数据结构-栈(带图)

目录 栈的概念 画图理解栈 栈的实现 fun.h fun.c main.c 栈的概念 栈&#xff08;Stack&#xff09;是一种基本的数据结构&#xff0c;其特点是只允许在同一端进行插入和删除操作&#xff0c;这一端被称为栈顶。遵循后进先出&#xff08;Last In, First Out, LIFO&#…

通俗易懂的策略模式讲解

什么是策略模式&#xff1f; 策略模式是一种设计模式&#xff0c;它允许你定义一系列的算法&#xff08;策略&#xff09;&#xff0c;并将每个算法封装成一个对象。这样&#xff0c;你可以轻松地切换不同的算法&#xff0c;而不需要改变原始代码。 一个简单的例子 假设你是…

OpenHarmony标准设备应用开发(二)——布局、动画与音乐

本章是 OpenHarmony 标准设备应用开发的第二篇文章。我们通过知识体系新开发的几个基于 OpenHarmony3.1 Beta 标准系统的样例&#xff1a;分布式音乐播放、传炸弹、购物车等样例&#xff0c;分别介绍下音乐播放、显示动画、动画转场&#xff08;页面间转场&#xff09;三个进阶…

Qwen学习笔记2:Qwen模型基于ReAct原理实现function calling

前言 这也是一篇笔记&#xff0c;再探索一下Qwen模型的function calling功能。 Qwen1.8B模型能够进行function calling功能吗&#xff1f; 我们使用之前的reason_prompt模板进行测试&#xff1a; PROMPT_REACT """ Use the following format:Question: the…

AWS RDS ElasticCache 监控可观测最佳实践

在当今的电子商务时代&#xff0c;一个高效、稳定的电商平台对于保持竞争力至关重要。数据库作为电商平台的核心支撑&#xff0c;其性能直接影响到用户体验和业务流畅度。本文将深入探讨如何在电商场景下通过观测云对亚马逊云科技 RDS&#xff08;MySQL&#xff09; 和 Elastic…

python怎么安装matplotlib

1、登陆官方网址“https://pypi.org/project/matplotlib/#description”&#xff0c;下载安装包。 2、选择合适的安装包&#xff0c;下载下来。 3、将安装包放置到python交互命令窗口的当前目录下。 4、打开windows的命令行窗口&#xff0c;通过"pip install"这个命令…

八分钟“手撕”包装类与泛型

目录 一、包装类 基本数据类型和对应的包装类 装箱和拆箱 【思考题】 二、泛型 什么是泛型 引出泛型 怎么定义泛型和使用泛型 裸类型(Raw Type) 擦除机制 额外&#xff0c;注意下列代码&#xff1a; 泛型的上界 泛型的接口应用 泛型方法 一、包装类 简单来…

OpenHarmony 3GPP协议开发深度剖析——一文读懂RIL

市面上关于终端&#xff08;手机&#xff09;操作系统在 3GPP 协议开发的内容太少了&#xff0c;即使 Android 相关的学习文档都很少&#xff0c;Android 协议开发书籍我是没有见过的。可能是市场需求的缘故吧&#xff0c;现在市场上还是前后端软件开发从业人员最多&#xff0c…

Hotcoin Research|玩赚WEB3:探索Apeiron:颠覆传统的区块链游戏,融合神话与现代玩法

1. 游戏概述 1.1 游戏类型与主题 Apeiron 是一款结合了上帝模拟、Roguelike、动作角色扮演&#xff08;ARPG&#xff09;和卡牌游戏元素的区块链游戏。这款游戏以独特的方式融合了多种游戏类型&#xff0c;提供了一个丰富多彩的神话宇宙&#xff0c;每个星系都受到不同现实世…

JVM知识点及面试题补充

JVM从软件层面屏蔽了不同操作系统的底层硬件与指令上的区别&#xff08;所谓的Java跨平台能力&#xff09; java中JRE&#xff08;java运行时环境&#xff09;包括java各种Libraries类库以及Java Virtual Machine&#xff08;Java虚拟机&#xff09;。 类加载子系统&#xff1…

Pycharm 编辑器编码格式设置

随笔 目录 1.背景 2. 修改编辑器编码设置 3. 最终修改 yml 写入 1.背景 由于写入yml文件中中文编码问题 ython 中讲数据写入yml 文件后&#xff0c;中文显示&#xff1a; "\u9A8C\u8BC1UDMA0_Tx_C0\u53D1\u9001\u6570\u636EUDMA0_Rx_C1\u65B9\u5411\u63A5\u6536\u65…

总结目前开源的视频生成/视频编辑大模型

Diffusion Models视频生成-博客汇总 前言&#xff1a;随着Sora的爆火&#xff0c;视频生成和视频编辑受到了越来越多的关注。这篇博客总结一下目前开源的视频生成和视频编辑的大模型&#xff0c;并分析他们各自的优缺点&#xff0c;以及在自己进行科研任务或者工作中应该如何选…

Linux网络配置全攻略:解读/etc/network/interfaces文件的精髓

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 Linux网络配置全攻略&#xff1a;解读/etc/network/interfaces文件的精髓 前言文件结构与基本概念配置网络接口的常用参数高级网络配置技巧实用工具与调试技巧实战案例与最佳实践 前言 在我们的日常生…

PM入门必备| 怎么写产品分析报告?

​小陪老师&#xff0c;产品经理是做些什么的呢&#xff1f;我去面试应该准备些什么呢&#xff1f; A: 首先要分清产品经理的类型&#xff0c;产品的面试需要准备的一般有Axure原型&#xff0c;需求文档&#xff0c;产品分析报告等&#xff0c;有些甚至需要展示项目经验。 tea…

“遥遥领先” time.sleep(6)?

日前&#xff0c;在一场万众瞩目的发布会上&#xff0c;华为自信满满地揭开了其大模型文生图技术的神秘面纱。然而&#xff0c;演示期间一个不经意间闪现的time.sleep(6)代码片段&#xff0c;如同投入平静湖面的一颗石子&#xff0c;激起了业界对于演示真实性与技术底蕴的热烈探…

Process Monitor下载安装使用教程(图文教程)超详细

「作者简介」&#xff1a;2022年北京冬奥会网络安全中国代表队&#xff0c;CSDN Top100&#xff0c;就职奇安信多年&#xff0c;以实战工作为基础对安全知识体系进行总结与归纳&#xff0c;著作适用于快速入门的 《网络安全自学教程》&#xff0c;内容涵盖系统安全、信息收集等…