Python自动化测试框架实战:从PO模式到pytest的完整构建指南 1. 项目概述为什么我们需要“深入理解”自动化测试如果你是一名开发工程师或者正在向测试开发岗位转型那么“Python自动化测试”这个词组对你来说一定不陌生。它几乎成了现代软件质量保障的代名词。但很多时候我们接触到的“自动化测试”可能只是零散的脚本——用Selenium录几个点击用requests发几个接口请求然后断言一下状态码。这离“深入理解”和“从基础到实践”还差得很远。真正的深入理解意味着你需要知道为什么选择某个框架如何设计一个可维护、可扩展的测试架构以及如何将自动化测试无缝集成到整个研发流程中让它不再是开发流程的累赘而是提效和保障质量的利器。Python之所以成为自动化测试领域的首选语言核心在于其极低的学习门槛、丰富的生态系统和强大的胶水能力。从Web UI自动化Selenium、移动端自动化Appium、接口测试requestspytest到性能测试locustPython都有成熟且活跃的社区支持。但工具只是工具如何将它们有机地组合起来构建一个健壮的测试框架并应对实际项目中复杂的场景如数据驱动、并发执行、测试报告、持续集成才是从“会用”到“精通”的关键跨越。这篇文章我将结合自己多年在一线搭建和维护自动化测试体系的实战经验带你从最基础的环境搭建和设计思想开始一步步构建一个完整的、可用于实际项目的自动化测试框架并分享那些在官方文档里找不到的“踩坑”心得和最佳实践。2. 自动化测试框架的整体设计与核心思想在动手写第一行代码之前我们必须先想清楚要构建一个什么样的框架。一个随意的脚本集合和一个设计良好的框架在项目后期维护成本上会有天壤之别。这里我们目标是搭建一个支持Web UI自动化和接口自动化的统一框架它应该具备以下核心特征结构清晰、易于维护、支持数据驱动、测试报告直观、易于集成到CI/CD。2.1 核心设计模式Page Object Model (PO模式)这是UI自动化测试的基石必须在一开始就确立。PO模式的核心思想是将测试代码操作逻辑和页面元素定位、操作细节分离开。每一个页面或页面中的一个功能模块被抽象成一个Page类这个类内部封装了该页面的所有元素定位器和对这些元素的操作方法如点击、输入。测试用例脚本则通过调用这些Page类提供的方法来完成业务操作而无需关心元素是如何被定位和操作的。为什么必须用PO模式假设你的登录按钮定位器从id“loginBtn”改成了class“.submit”。如果没有PO模式你需要在几十上百个测试用例中逐一修改这个定位器这是维护的噩梦。而使用了PO模式你只需要在对应的LoginPage类里修改一次login_button这个属性的定位方式所有调用LoginPage.login()的测试用例都无需改动。这极大地提升了代码的可维护性和复用性。2.2 测试框架选型为什么是pytest而不是unittest虽然很多教程从unittest开始因为它属于Python标准库开箱即用。但对于一个追求效率和强大功能的现代测试框架我强烈推荐pytest。原因如下更简洁的语法pytest使用普通的assert语句进行断言比unittest的self.assertEqual()等更符合Pythonic风格写起来更自然。强大的Fixture机制这是pytest的王牌功能。Fixture可以为你提供测试所需的环境准备和清理工作例如启动/关闭浏览器、初始化数据库连接、登录获取token等。你可以通过pytest.fixture装饰器定义并在测试函数中直接通过参数调用管理测试生命周期非常优雅。丰富的插件生态pytest-html可以生成美观的HTML报告pytest-xdist支持分布式并行测试以加快执行速度pytest-rerunfailures可以对失败用例自动重试。这些插件能极大增强框架的能力。参数化测试非常方便pytest.mark.parametrize装饰器让数据驱动测试变得极其简单直观。基于以上原因我们的框架将以pytest作为测试运行和组织的核心。2.3 项目目录结构设计一个清晰、标准的目录结构是框架可维护性的第一步。我推荐如下结构automation_framework/ ├── common/ # 公共组件层 │ ├── __init__.py │ ├── base_page.py # 所有Page类的基类封装公共方法 │ ├── logger.py # 自定义日志模块 │ ├── config.py # 配置文件读取如环境URL、数据库配置 │ └── webdriver_manager.py # 浏览器驱动管理 ├── pages/ # 页面对象层 │ ├── __init__.py │ ├── login_page.py │ └── home_page.py ├── test_cases/ # 测试用例层 │ ├── __init__.py │ ├── conftest.py # pytest的本地Fixture配置 │ ├── test_ui/ # UI测试用例 │ └── test_api/ # 接口测试用例 ├── test_data/ # 测试数据层 │ ├── __init__.py │ └── users.json # 数据文件如JSON, YAML, Excel ├── reports/ # 测试报告输出目录 ├── logs/ # 日志文件输出目录 ├── requirements.txt # 项目依赖包列表 └── pytest.ini # pytest配置文件这个结构体现了分层思想common放工具pages放页面对象test_cases放测试逻辑test_data放外部数据。各司其职互不干扰。3. 环境搭建与核心组件实战理论说再多不如动手。我们现在就从零开始搭建这个框架的核心骨架。3.1 基础环境与依赖安装首先确保你安装了Python建议3.8及以上版本。然后在项目根目录下创建requirements.txt文件并填入核心依赖# 测试核心 pytest7.0.0 pytest-html3.0.0 pytest-xdist3.0.0 pytest-rerunfailures10.0 pytest-ordering0.6 # Web UI自动化 selenium4.0.0 webdriver-manager3.8.0 # 自动管理浏览器驱动强烈推荐 # 接口自动化 requests2.28.0 requests-toolbelt0.10.0 # 数据管理与报告 PyYAML6.0 openpyxl3.0.0 # 如需处理Excel数据 allure-pytest2.9.0 # 可选用于生成Allure报告 # 工具类 python-dotenv0.20.0 # 管理环境变量在终端执行pip install -r requirements.txt即可一键安装所有依赖。webdriver-manager这个库是个神器它能自动下载和匹配你本地浏览器版本的驱动彻底告别手动下载和配置chromedriver路径的烦恼。3.2 编写核心基础类BasePage在common/base_page.py中我们创建所有页面对象的父类。它封装了Selenium的常用操作并提供日志、等待等增强功能。import logging from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException, NoSuchElementException class BasePage: 所有Page类的基类 def __init__(self, driver): self.driver driver self.logger logging.getLogger(__name__) self.wait WebDriverWait(driver, timeout10, poll_frequency0.5) def find_element(self, locator): 查找单个元素加入显式等待和日志 try: self.logger.info(f正在查找元素: {locator}) element self.wait.until(EC.presence_of_element_located(locator)) self.logger.info(f元素查找成功: {locator}) return element except TimeoutException: self.logger.error(f元素查找超时: {locator}) # 这里可以附加截图操作便于调试 raise def click(self, locator): 点击元素 element self.find_element(locator) self.logger.info(f点击元素: {locator}) element.click() def input_text(self, locator, text): 输入文本并先清空输入框 element self.find_element(locator) element.clear() self.logger.info(f在元素 {locator} 中输入文本: {text}) element.send_keys(text) def get_text(self, locator): 获取元素文本 element self.find_element(locator) text element.text self.logger.info(f获取元素 {locator} 的文本: {text}) return text def is_element_visible(self, locator, timeout5): 判断元素是否可见 try: WebDriverWait(self.driver, timeout).until(EC.visibility_of_element_located(locator)) return True except TimeoutException: return False实操心得显式等待是必须的不要用time.sleep()它不稳定且低效。使用WebDriverWait配合expected_conditions是行业最佳实践。日志至关重要在每一个关键操作查找、点击、输入前后都加上日志当测试失败时查看日志文件能快速定位到是哪一步出了问题而不是盲目地调试。统一的定位器格式建议使用(By.ID, “username”)或(By.XPATH, “//button[type‘submit’]”)这种元组形式清晰且易于管理。3.3 实现第一个Page对象LoginPage有了BasePage实现具体的页面就非常轻松了。在pages/login_page.py中from selenium.webdriver.common.by import By from common.base_page import BasePage class LoginPage(BasePage): 登录页面模型 # 定位器将页面元素集中管理 USERNAME_INPUT (By.ID, “username”) PASSWORD_INPUT (By.ID, “password”) LOGIN_BUTTON (By.XPATH, “//button[type‘submit’]”) ERROR_MSG_SPAN (By.CLASS_NAME, “error-message”) def __init__(self, driver): super().__init__(driver) self.driver driver def open(self, url): 打开登录页面 self.logger.info(f“打开登录页面: {url}”) self.driver.get(url) return self def enter_username(self, username): 输入用户名 self.input_text(self.USERNAME_INPUT, username) return self # 支持链式调用 def enter_password(self, password): 输入密码 self.input_text(self.PASSWORD_INPUT, password) return self def click_login(self): 点击登录按钮 self.click(self.LOGIN_BUTTON) def get_error_message(self): 获取错误提示信息 if self.is_element_visible(self.ERROR_MSG_SPAN): return self.get_text(self.ERROR_MSG_SPAN) return None def login(self, username, password): 完整的登录业务流程 self.enter_username(username) self.enter_password(password) self.click_login()注意事项链式调用像enter_username().enter_password().click_login()这样的写法可以让测试用例更简洁。通过在页面方法中return self来实现。业务方法封装login()方法封装了完整的登录流程这是PO模式的进阶用法。测试用例可以直接调用page.login(“user”, “pass”)进一步简化用例代码并保证登录操作的一致性。4. 编写与组织测试用例接下来我们在test_cases/test_ui/下创建我们的第一个UI测试用例文件test_login.py。4.1 使用Fixture管理浏览器生命周期首先在test_cases/conftest.py中定义全局Fixture。conftest.py是pytest的本地插件文件其中定义的Fixture可以被该目录及其子目录下的所有测试文件使用。import pytest from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager from common.logger import setup_logger # 假设已实现日志配置 pytest.fixture(scope“function”) # 每个测试函数执行一次 def driver(): 提供WebDriver实例的Fixture # 使用webdriver-manager自动管理驱动 service Service(ChromeDriverManager().install()) options webdriver.ChromeOptions() options.add_argument(‘--headless’) # 无头模式适合CI环境 options.add_argument(‘--no-sandbox’) options.add_argument(‘--disable-dev-shm-usage’) driver webdriver.Chrome(serviceservice, optionsoptions) driver.implicitly_wait(5) # 设置隐式等待备用 driver.maximize_window() yield driver # 将driver实例提供给测试用例 # 测试结束后执行清理工作 driver.quit() print(“浏览器已关闭”) pytest.fixture def login_page(driver): 提供登录页面实例的Fixture from pages.login_page import LoginPage page LoginPage(driver) page.open(“https://your-test-app.com/login”) # 从配置中读取更好 return page关键点解析scope“function”表示这个Fixture在每个测试函数开始时创建函数结束时清理。这是最常用的范围确保测试之间的隔离。yield这是Fixture的核心机制。yield之前的代码是“设置”部分yield之后的代码是“清理”部分。测试函数执行时实际接收到的是yield返回的对象这里是driver。无头模式--headless参数让浏览器在后台运行不显示GUI极大节省资源且适合在服务器CI/CD流水线中运行。调试时可以去掉这个参数以便观察浏览器操作。4.2 编写第一个测试用例现在在test_login.py中编写测试用例import pytest import logging class TestLogin: 登录功能测试集 def test_login_success(self, login_page): 测试正常登录成功 # 使用封装好的业务方法 login_page.login(username“standard_user”, password“secret_sauce”) # 断言登录后应跳转到首页通过检查首页特定元素或URL来判断 assert “inventory.html” in login_page.driver.current_url logging.info(“登录成功测试通过”) pytest.mark.parametrize(“username, password, expected_error”, [ (“”, “secret_sauce”, “Username is required”), (“standard_user”, “”, “Password is required”), (“wrong_user”, “wrong_pass”, “Username and password do not match”), ]) def test_login_failure(self, login_page, username, password, expected_error): 测试各种登录失败场景 - 数据驱动 login_page.enter_username(username) login_page.enter_password(password) login_page.click_login() # 断言错误信息符合预期 actual_error login_page.get_error_message() assert actual_error expected_error, f“期望错误信息‘{expected_error}’实际得到‘{actual_error}’” def test_login_with_invalid_element(self, driver): 测试页面元素异常场景不使用login_page fixture # 直接访问一个不存在的页面测试框架的容错性 driver.get(“https://your-test-app.com/invalid”) # 这里可以测试BasePage中find_element的异常处理是否正常 # 例如尝试定位一个不存在的元素应抛出TimeoutException with pytest.raises(Exception): from selenium.webdriver.common.by import By driver.find_element(By.ID, “non-existent-element”)实操心得与技巧使用pytest.mark.parametrize进行数据驱动这是将测试数据与测试逻辑分离的优雅方式。如上例一个测试函数可以覆盖多组输入和预期输出极大减少了代码重复。测试数据可以进一步外置到JSON或YAML文件中。断言要明确断言失败时的提示信息要清晰。使用assert a b, f“提示信息”的格式可以在测试报告里直接看到是什么值不符合预期。测试用例的独立性每个测试用例应该可以独立运行且不依赖于其他用例的执行状态。这就是为什么我们把浏览器启动/关闭放在function级别的Fixture里。如果测试间有依赖如先登录才能做后续操作应该通过Fixture的依赖关系或在一个用例内完成整个流程而不是假设上一个用例已登录。4.3 接口自动化测试实战UI自动化测试的是前端交互而接口自动化测试的是后端服务的逻辑它通常更稳定、执行更快。我们在test_cases/test_api/下创建test_user_api.py。首先在common/下创建一个api_client.py作为接口请求的封装import requests import logging from common.config import get_config # 假设有读取配置的模块 class ApiClient: 封装HTTP请求的客户端 def __init__(self, base_urlNone): self.session requests.Session() self.base_url base_url or get_config(“API_BASE_URL”) self.logger logging.getLogger(__name__) # 可以在这里设置公共请求头如Content-Type, Authorization self.session.headers.update({“Content-Type”: “application/json”}) def set_auth_token(self, token): 设置认证Token self.session.headers.update({“Authorization”: f“Bearer {token}”}) def request(self, method, endpoint, **kwargs): 发送请求并加入日志和基础校验 url f“{self.base_url}{endpoint}” self.logger.info(f“发送请求: {method} {url}”) self.logger.debug(f“请求参数: {kwargs}”) try: response self.session.request(method, url, **kwargs) self.logger.info(f“收到响应: 状态码{response.status_code}”) self.logger.debug(f“响应内容: {response.text}”) except requests.exceptions.ConnectionError as e: self.logger.error(f“网络连接错误: {e}”) raise except requests.exceptions.Timeout as e: self.logger.error(f“请求超时: {e}”) raise # 可以在这里添加对响应状态的通用断言例如非2xx状态码记录警告 if not response.ok: self.logger.warning(f“API请求失败: {response.status_code} - {response.text}”) return response # 便捷方法 def get(self, endpoint, paramsNone, **kwargs): return self.request(“GET”, endpoint, paramsparams, **kwargs) def post(self, endpoint, dataNone, jsonNone, **kwargs): return self.request(“POST”, endpoint, datadata, jsonjson, **kwargs) # ... 可以继续封装put, delete等方法然后编写接口测试用例import pytest from common.api_client import ApiClient class TestUserApi: 用户相关接口测试 pytest.fixture def api_client(self): 提供API客户端Fixture client ApiClient() # 这里可以执行全局的初始化比如先调用登录接口获取token # login_resp client.post(“/login”, json{“username”: “admin”, “password”: “admin”}) # client.set_auth_token(login_resp.json()[“token”]) return client def test_get_user_list(self, api_client): 测试获取用户列表接口 response api_client.get(“/api/users”, params{“page”: 1, “size”: 10}) # 断言状态码 assert response.status_code 200 # 断言响应体结构 json_data response.json() assert “data” in json_data assert “total” in json_data assert isinstance(json_data[“data”], list) # 断言数据内容 assert len(json_data[“data”]) 10 pytest.mark.parametrize(“user_data”, [ {“name”: “Alice”, “email”: “aliceexample.com”}, {“name”: “Bob”, “email”: “bobexample.com”}, ]) def test_create_user(self, api_client, user_data): 测试创建用户接口 - 参数化 response api_client.post(“/api/users”, jsonuser_data) assert response.status_code 201 created_user response.json() assert created_user[“name”] user_data[“name”] assert created_user[“email”] user_data[“email”] assert “id” in created_user # 验证返回了用户ID接口测试关键点会话管理使用requests.Session()可以自动保持cookies模拟用户会话状态对于需要登录的接口序列测试非常方便。响应断言接口测试的断言通常包括状态码、响应体结构、字段值、数据类型等。可以使用jsonschema库来更严格地校验JSON结构是否符合预期。测试数据清理对于创建、修改数据的测试最好在测试完成后清理测试数据如删除刚创建的用户以免影响后续测试或污染数据库。这可以通过在Fixture的清理阶段yield之后或使用pytest的finalizer来实现。5. 测试执行、报告与高级配置框架和用例写好了如何运行并得到漂亮的报告呢5.1 使用pytest.ini进行配置在项目根目录创建pytest.ini文件统一管理pytest的运行配置[pytest] # 指定测试文件的位置和命名规则 testpaths test_cases python_files test_*.py python_classes Test* python_functions test_* # 添加命令行默认选项 addopts -v # 详细输出 --htmlreports/report.html # 生成HTML报告 --self-contained-html # 生成独立的HTML文件CSS内联 --capturesys # 捕获输出 --maxfail5 # 失败5个用例后停止 --reruns 2 # 失败重试2次需要pytest-rerunfailures插件 --reruns-delay 1 # 重试间隔1秒 # 定义标记用于分类运行测试 markers smoke: 冒烟测试用例 regression: 回归测试用例 slow: 执行缓慢的测试用例5.2 运行测试与生成报告在终端中进入项目根目录执行以下命令运行所有测试pytest运行指定目录pytest test_cases/test_ui/运行标记为smoke的测试pytest -m smoke运行包含“login”关键字的测试pytest -k login并行运行测试利用多核CPUpytest -n auto(需要pytest-xdist插件)执行完成后会在reports/目录下生成report.html文件。用浏览器打开可以看到一个包含测试通过率、执行时间、失败用例详情和日志的详细报告非常直观。5.3 集成到CI/CD流水线自动化测试只有集成到持续集成/持续部署流程中才能最大化其价值。以GitHub Actions为例可以在项目根目录创建.github/workflows/test.ymlname: Python Automation Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - name: Set up Python uses: actions/setup-pythonv4 with: python-version: ‘3.9’ - name: Install dependencies run: | pip install -r requirements.txt - name: Install Chrome for UI Tests run: | sudo apt-get update sudo apt-get install -y chromium-browser - name: Run Tests with pytest run: | # 在CI环境中通常以无头模式运行UI测试 # 可以设置环境变量来控制是否运行UI测试 pytest --htmlreports/report.html --self-contained-html - name: Upload Test Report uses: actions/upload-artifactv3 if: always() # 即使测试失败也上传报告 with: name: html-report path: reports/这样每次代码推送或发起拉取请求时都会自动运行测试套件并将测试报告作为制品保存方便查看。6. 常见问题排查与实战技巧实录在实际项目中你会遇到各种各样的问题。这里分享一些高频问题的解决思路和技巧。6.1 元素定位失败自动化测试的头号敌人问题现象NoSuchElementException或TimeoutException。排查思路与解决方案等待问题这是最常见的原因。页面还没加载完脚本就去操作元素了。解决方案坚持使用WebDriverWait显式等待而不是time.sleep或隐式等待。等待条件要选对比如等待元素可点击(element_to_be_clickable)比等待元素存在(presence_of_element_located)更严格。# 好等待元素可交互 wait.until(EC.element_to_be_clickable(locator)).click() # 不够好只等待元素存在可能还不可点击 wait.until(EC.presence_of_element_located(locator)).click()iframe/Shadow DOM元素嵌套在iframe或Shadow DOM内部直接定位会失败。解决方案先切换到对应的iframe或Shadow Root。# 切换iframe iframe driver.find_element(By.TAG_NAME, “iframe”) driver.switch_to.frame(iframe) # 操作iframe内元素... driver.switch_to.default_content() # 切回来 # 处理Shadow DOM (Selenium 4) shadow_host driver.find_element(By.CSS_SELECTOR, “#shadow-host”) shadow_root shadow_host.shadow_root inner_element shadow_root.find_element(By.CSS_SELECTOR, “.inner-element”)动态ID或类名前端框架如React, Vue生成的元素标识符可能每次都会变化。解决方案使用相对定位如通过文本内容、相邻元素关系、属性部分匹配等。# 避免使用动态ID # (By.ID, “button-12345”) # 下次可能变成 button-67890 # 推荐使用相对稳定的属性或XPath (By.XPATH, “//button[contains(text(), ‘提交’)]”) (By.XPATH, “//div[class‘container’]//input[placeholder‘请输入用户名’]”)页面结构变化这是UI自动化固有的维护成本。解决方案这就是PO模式的价值所在。所有定位器集中在Page类中一旦变化只需修改一处。同时与前端开发建立沟通机制约定尽量避免频繁改动核心元素的定位方式。6.2 测试不稳定Flaky Tests问题现象测试用例有时成功有时失败没有规律。解决策略增加重试机制使用pytest-rerunfailures插件对失败用例自动重试几次。优化等待策略检查并优化所有显式等待的条件和超时时间。隔离测试环境确保测试数据独立用例之间没有依赖。使用数据库事务或在setup/teardown中清理测试数据。禁用动画和视频某些CSS动画或自动播放视频可能干扰元素交互。options.add_experimental_option(“prefs”, { “profile.managed_default_content_settings.images”: 2, # 可选禁用图片加载加速 })6.3 如何管理测试数据硬编码在测试脚本里的数据是坏味道。推荐几种方式外部数据文件将测试数据放在JSON、YAML或CSV文件中。# test_data/login_users.yaml valid_users: - username: “user1” password: “pass1” expected: “success” - username: “user2” password: “pass2” expected: “success” # 在测试中读取 import yaml with open(“test_data/login_users.yaml”) as f: test_data yaml.safe_load(f) pytest.mark.parametrize(“user”, test_data[“valid_users”]) def test_login(self, login_page, user): login_page.login(user[“username”], user[“password”]) # ... 断言动态生成数据对于需要唯一性的数据如用户名、邮箱使用faker库动态生成。from faker import Faker fake Faker() unique_email fake.email()数据库夹具对于复杂的数据场景可以使用pytest的Fixture来在测试前向数据库插入准备好的数据测试后清理。6.4 提升测试执行速度并行执行使用pytest-xdist插件pytest -n auto。测试分组将快速的核心测试冒烟测试和慢速的端到端测试分开。使用pytest的标记mark功能快速测试可以频繁运行慢速测试可以只在夜间或发布前运行。优化Fixture作用域如果一个Fixture创建成本很高如启动一个复杂的Docker服务但多个测试用例都需要且不会相互影响可以将其作用域设置为scope“session”或scope“module”这样在整个测试会话或模块中只创建一次。API测试优先尽量用接口测试覆盖业务逻辑因为接口测试比UI测试快一个数量级也更稳定。UI测试重点覆盖核心的用户交互流程。7. 从框架到平台未来的演进方向当你熟练掌握了上述内容搭建的自动化测试框架稳定运行后你可能会思考如何更进一步。这时可以考虑以下几个方向测试报告平台化将生成的HTML报告上传到内部服务器并通过一个简单的Web页面进行聚合展示形成测试历史趋势图、通过率看板等。测试用例管理引入Allure报告框架它能生成非常美观、交互性强的测试报告并且支持对测试用例进行分层、标记严重程度、附加截图和日志。行为驱动开发尝试pytest-bdd用自然语言Gherkin语法编写测试场景.feature文件让产品、测试、开发对需求的理解保持一致。视觉回归测试对于UI样式有严格要求的项目可以集成像Applitools、Percy这样的视觉对比工具自动检测UI上的像素级变化。移动端自动化将Appium集成到你的框架中用同一套Python代码和设计模式PO模式来管理iOS和Android应用的自动化测试。自动化测试不是一个一蹴而就的项目而是一个需要持续投入和维护的工程。从编写第一个稳定的测试用例开始到构建一个健壮的框架再到将其融入团队的工作流并不断优化每一步都需要对工具、设计和团队协作有深入的理解。希望这篇从基础到实践的长文能为你打下坚实的基础并启发你构建出最适合自己团队的自动化测试解决方案。记住最好的框架不是功能最全的而是最适合你当前团队和项目状态的。