Day1用DeepSeek学习自动化测试

一、单元测试示例详解

代码目标:测试计算器的加法函数 add(a, b)
# calculator.py
def add(a, b):
    return a + b
# test_calculator.py
import pytest
from calculator import add  # 导入被测函数

def test_add_normal():
    assert add(2, 3) == 5   # 断言:验证2+3是否等于5

def test_add_negative():
    assert add(-1, -1) == -2  # 测试负数相加

def test_add_string():
    assert add("Hello", "World") == "HelloWorld"  # 故意测试字符串拼接(会失败)
关键设计点
  1. 为什么用 assert 语句?
    assert 是Python的断言关键字,用于验证实际结果是否符合预期。如果断言失败,测试会自动标记为失败。

  2. 为什么要分开多个测试函数?
    每个测试函数对应一个独立场景(如正常值、负数),避免用例间相互影响,提升可读性。

  3. 为什么第三个测试会失败?
    原函数 add 设计上是数值加法,但Python的 + 运算符对字符串会拼接。这里故意演示“测试用例与需求不符”的场景,提醒开发者注意需求边界。


二、接口测试示例详解

代码目标:测试登录接口是否能正确处理用户名和密码。
# test_api_login.py
import requests

def test_login():
    # 1. 定义接口地址和请求数据
    url = "http://api.example.com/login"
    data = {"username": "test_user", "password": "123456"}
    
    # 2. 发送POST请求
    response = requests.post(url, json=data)  # 使用json参数自动序列化为JSON
    
    # 3. 验证HTTP状态码是否为200(成功)
    assert response.status_code == 200, "接口请求失败"
    
    # 4. 解析返回的JSON数据
    result = response.json()
    
    # 5. 验证业务逻辑状态码和返回字段
    assert result["code"] == 0, "登录失败"  # 假设code=0表示成功
    assert "token" in result, "未返回token"  # 验证是否返回身份令牌
关键设计点
  1. 为什么用 requests.post
    requests 是Python最常用的HTTP库,post 方法用于发送POST请求,json=data 会自动将字典转换为JSON格式。

  2. 为什么要检查状态码 200
    HTTP状态码200表示请求成功。如果返回404(未找到)或500(服务器错误),说明接口本身有问题。

  3. 为什么要验证 codetoken
    状态码200只能说明接口可达,实际业务逻辑是否正确需通过返回的JSON数据判断(如 code=0 表示成功,token 是登录凭证)。


三、UI测试示例详解(Selenium)

代码目标:用浏览器自动化测试百度搜索功能。
# test_baidu_search.py
from selenium import webdriver
from selenium.webdriver.common.by import By  # 定位元素的策略(如ID、XPath)
import time

def test_search():
    # 1. 启动浏览器并打开百度
    driver = webdriver.Chrome()  # 需提前配置ChromeDriver路径
    driver.get("https://www.baidu.com")
    
    # 2. 定位搜索框并输入关键词
    search_box = driver.find_element(By.ID, "kw")  # 通过元素ID定位
    search_box.send_keys("自动化测试")  # 模拟键盘输入
    
    # 3. 点击“百度一下”按钮
    search_button = driver.find_element(By.ID, "su")
    search_button.click()  # 模拟鼠标点击
    
    # 4. 等待2秒(临时方案,实际应用需优化)
    time.sleep(2)
    
    # 5. 验证搜索结果页标题是否包含关键词
    assert "自动化测试" in driver.title
    
    # 6. 关闭浏览器
    driver.quit()
关键设计点
  1. 为什么用 By.ID 定位元素?
    元素的ID通常是唯一且稳定的(如百度搜索框的ID是kw),比XPath更简洁且不易受页面结构变化影响。

  2. 为什么要用 time.sleep(2)
    等待页面加载完成。但这是临时方案,更好的做法是用显式等待(如 WebDriverWait),避免因网络延迟导致失败。

  3. 为什么验证 driver.title
    搜索结果页的标题通常会包含搜索关键词,这是验证搜索是否成功的简单方式。


四、通用设计原则总结

1. 模块化与复用性
  • 封装函数:将重复操作(如登录)封装成函数,减少代码冗余。
  • 示例
    def login(driver, username, password):
        driver.find_element(By.ID, "username").send_keys(username)
        driver.find_element(By.ID, "password").send_keys(password)
        driver.find_element(By.ID, "submit").click()
    
2. 数据驱动测试
  • 分离数据与逻辑:用外部文件(如JSON)管理测试数据,支持多场景测试。
  • 示例
    import json
    with open("test_data.json") as f:
        test_cases = json.load(f)["test_cases"]
    for case in test_cases:
        test_login(case["username"], case["password"])
    
3. 稳定性与容错
  • 显式等待:替代 time.sleep,动态等待元素出现。
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    element = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.ID, "kw"))
    )
    

五、常见疑问解答

Q1:为什么单元测试用 pytest 而不是 unittest
  • 答案pytest 更简洁(无需继承类)、支持参数化测试、报告更友好,且兼容 unittest
Q2:UI测试中如果元素ID变了怎么办?
  • 答案:使用 Page Object模式,将元素定位集中在单独文件中,修改时只需调整一处。
    示例
    # pages/login_page.py
    class LoginPage:
        username_input = (By.ID, "username")
        password_input = (By.ID, "password")
        submit_button = (By.ID, "submit")
    
Q3:接口测试如何模拟异常场景(如错误密码)?
  • 答案:设计多组测试数据,验证接口的错误处理逻辑。
    示例
    # 测试错误密码
    data = {"username": "test_user", "password": "wrong"}
    response = requests.post(url, json=data)
    assert response.json()["code"] == 1001  # 假设1001表示密码错误
    
<think>好的,用户想要用Python编写一个国内旅游攻略自动化生成软件,集成DeepSeek API、和风天气API和高德地图API。首先,我需要理清楚用户的需求和各个API的功能。用户提到的功能流程图显示,用户输入城市名称和旅游天数后,系统需要获取景点和天气信息,智能拆分景点,关联附近美食,最后生成时间表。 首先,用户可能是一个开发者或者有一定编程基础的人,想要自动化生成旅游攻略,节省时间和精力。他们的真实需求不仅是整合多个API,还要确保整个流程顺畅,数据准确,并且生成的时间表合理实用。可能还有一些未明确的需求,比如错误处理、用户体验优化等。 接下来,我需要考虑各个API的使用方法。和风天气API用于获取天气数据,需要注册获取API key,并了解其返回的数据结构,比如是否有每日天气的daily字段,之前用户提到的KeyError可能就是因为数据结构不符,所以需要注意这点。高德地图API用于获取景点和附近美食,需要调用他们的POI搜索和周边推荐接口。DeepSeek API可能用于自然语言处理,生成旅游建议或时间表描述,但用户没有具体说明用途,可能需要进一步确认。 然后,设计整个系统的架构。用户输入城市和天数后,系统需要依次调用天气API获取天气情况,高德地图获取景点列表,然后根据天数智能拆分景点,可能要考虑景点之间的距离、开放时间、用户评分等因素。接着,为每个景点查找附近的美食,最后生成详细的时间安排表。 在智能拆分景点部分,可能需要算法来优化路线,比如基于地理位置聚类,或者使用旅行商问题(TSP)的近似解法,但考虑到时间限制,可能需要简化处理,比如按区域划分或者热门程度排序。关联附近美食需要调用高德地图的周边搜索接口,传入景点坐标和餐饮类别的关键词。 生成时间表时,需要合理分配每天的景点数量,考虑交通时间和用餐时间,可能需要预设每个景点的游览时长,并结合天气情况调整户外活动安排。 错误处理也很重要,比如API调用失败、数据解析错误、网络问题等,需要添加try-except块,友好的错误提示,并可能重试机制。同时,用户可能需要保存生成的攻略,或者导出为PDF、Excel格式,但用户没有提到,可能需要后续扩展。 代码结构方面,使用函数模块化每个步骤:获取天气、获取景点、拆分景点、查找美食、生成时间表。使用配置文件管理API密钥,避免硬编码。可能需要异步处理API请求以提高效率,但同步请求更简单,适合初步实现。 测试环节需要模拟不同城市的输入,检查各个API的响应是否正确,处理边界情况,比如旅游天数过多或过少,城市名称错误等情况。用户可能在实际使用中遇到各种问题,比如高德API的限流,需要处理HTTP状态码和响应数据中的错误码。 最后,用户可能需要一个简单的命令行界面或图形界面,但根据问题描述,可能只需要核心功能的实现。需要编写文档说明如何配置API密钥和运行程序。 总结下来,开发步骤包括:注册API获取密钥,编写各个API的调用函数,实现数据处理和逻辑整合,设计智能拆分算法,处理异常情况,测试并优化用户体验。需要确保代码的可维护性和扩展性,方便后续添加新功能或更换API服务商。</think>--- ### **国内旅游攻略自动化生成软件架构设计** 以下基于Python实现多API集成方案,使用模块化设计确保可扩展性。 --- ### **一、技术选型** | 组件 | 用途 | 官方文档 | |---------------|-------------------------------|-------------------------------------| | DeepSeek API | 生成自然语言描述的行程建议 | [DeepSeek开发者平台](https://platform.deepseek.com/) | | 和风天气API | 获取指定日期天气数据 | [和风天气开发文档](https://dev.qweather.com/) | | 高德地图API | 获取景点POI/周边美食/路线规划 | [高德开放平台](https://lbs.amap.com/) | --- ### **二、核心功能实现代码** #### **1. 基础配置** ```python import requests import json from datetime import datetime, timedelta # API配置 CONFIG = { "qweather": { "key": "YOUR_QWEATHER_KEY", "daily_url": "https://api.qweather.com/v7/weather/7d" }, "amap": { "key": "YOUR_AMAP_KEY", "poi_url": "https://restapi.amap.com/v3/place/text", "around_url": "https://restapi.amap.com/v3/place/around" }, "deepseek": { "key": "YOUR_DEEPSEEK_KEY", "api_url": "https://api.deepseek.com/v1/chat/completions" } } ``` #### **2. 天气获取模块** ```python def get_weather(city: str, date: str) -> dict: """ 获取指定城市日期的天气数据 返回格式:{"date": "2024-05-20", "temp": "20~28℃", "weather": "晴"} """ params = { "location": get_city_code(city), # 需实现城市名转adcode "key": CONFIG["qweather"]["key"] } try: response = requests.get(CONFIG["qweather"]["daily_url"], params=params) data = response.json() if "daily" not in data: raise ValueError("API返回数据异常") for day_data in data["daily"]: if day_data["fxDate"] == date: return { "date": date, "temp": f"{day_data['tempMin']}~{day_data['tempMax']}℃", "weather": day_data["textDay"] } return {"error": "超出预报范围"} except Exception as e: return {"error": str(e)} ``` #### **3. 景点与美食获取模块** ```python def get_attractions(city: str, max_num=20) -> list: """获取城市热门景点列表""" params = { "key": CONFIG["amap"]["key"], "keywords": "景点", "city": city, "types": "风景名胜", "output": "json" } response = requests.get(CONFIG["amap"]["poi_url"], params=params) return sorted( response.json()["pois"], key=lambda x: float(x["biz_ext"]["rating"] or 0), reverse=True )[:max_num] def find_nearby_food(location: str, radius=1000) -> list: """根据坐标查找周边美食(高德坐标系)""" params = { "key": CONFIG["amap"]["key"], "location": location, "radius": radius, "types": "餐饮", "sortrule": "distance" } response = requests.get(CONFIG["amap"]["around_url"], params=params) return response.json()["pois"] ``` #### **4. 智能行程生成模块** ```python def generate_itinerary(spots: list, days: int) -> list: """ 景点智能拆分算法 输入:排序后的景点列表、旅游天数 输出:按天分组的景点集合 """ # 基础版本:简单均分(后续可升级为基于距离聚类) avg = len(spots) // days return [spots[i*avg : (i+1)*avg] for i in range(days)] ``` #### **5. DeepSeek AI增强模块** ```python def generate_description(itinerary: list) -> str: """使用DeepSeek生成自然语言描述""" headers = { "Authorization": f"Bearer {CONFIG['deepseek']['key']}", "Content-Type": "application/json" } prompt = f"请将以下行程转化为旅游攻略:{json.dumps(itinerary)}" payload = { "model": "deepseek-chat", "messages": [{"role": "user", "content": prompt}] } response = requests.post(CONFIG["deepseek"]["api_url"], headers=headers, json=payload) return response.json()["choices"][0]["message"]["content"] ``` --- ### **三、主程序工作流** ```python def main(): # 用户输入 city = input("请输入旅游城市:") days = int(input("请输入旅游天数:")) start_date = input("请输入出发日期(YYYY-MM-DD):") # 获取基础数据 weather_data = [get_weather(city, (datetime.strptime(start_date, "%Y-%m-%d") + timedelta(days=i)).strftime("%Y-%m-%d")) for i in range(days)] spots = get_attractions(city) # 生成行程 daily_spots = generate_itinerary(spots, days) # 关联美食 full_itinerary = [] for day_idx, spots_group in enumerate(daily_spots): day_plan = { "date": weather_data[day_idx]["date"], "weather": weather_data[day_idx]["weather"], "spots": [] } for spot in spots_group: location = spot["location"].split(",") foods = find_nearby_food(f"{location[0]},{location[1]}") day_plan["spots"].append({ "name": spot["name"], "address": spot["address"], "recommend_foods": [food["name"] for food in foods[:3]] }) full_itinerary.append(day_plan) # AI生成描述 print(generate_description(full_itinerary)) if __name__ == "__main__": main() ``` --- ### **四、关键优化方向** 1. **路线智能优化** ```python # 使用高德路径规划API计算景点间距离 def get_route_time(origin: str, destination: str) -> float: params = { "key": CONFIG["amap"]["key"], "origin": origin, "destination": destination, "strategy": "2" # 最短距离策略 } response = requests.get("https://restapi.amap.com/v3/direction/driving", params=params) return float(response.json()["route"]["paths"][0]["duration"])/60 # 返回分钟数 ``` 2. **负载均衡设计** ```python # 动态调整每日景点数量 def dynamic_schedule(spots: list, days: int) -> list: # 根据景点评分+预计游览时间进行加权计算 weighted_spots = [] for spot in spots: time = 2.0 if "公园" in spot["type"] else 1.5 # 基础时间预估 weighted_spots.append( (spot, float(spot["biz_ext"]["rating"])*0.7 + time*0.3) ) # 使用背包算法进行分配 # ...(具体实现代码) ``` 3. **异常处理增强** ```python # 在API调用处添加重试机制 from tenacity import retry, stop_after_attempt, wait_fixed @retry(stop=stop_after_attempt(3), wait=wait_fixed(1)) def safe_api_call(url: str, params: dict): response = requests.get(url, params=params, timeout=5) response.raise_for_status() return response.json() ``` --- ### **五、部署与扩展建议** 1. **前端界面**:使用PyQt/Gradio快速搭建操作界面 2. **数据缓存**:对天气/景点数据建立本地缓存(SQLite) 3. **输出格式**:支持生成PDF/Excel文件 4. **实时更新**:添加微信/邮件通知功能 建议先从核心功能入手,逐步迭代优化。实际开发中需特别注意各API的调用频率限制,建议添加使用量监控模块。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值