Bootstrap

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表示密码错误
    
;