Selenium 文件上传和下载(千字长文)

Selenium 文件上传和下载的实战指南

在自动化测试中,文件上传和下载是验证功能完整性的重要环节。Selenium 作为主流的 Web 自动化工具,提供了丰富的 API 来处理这些操作。本文将通过实际案例和代码示例,带您系统掌握 Selenium 文件上传和下载的核心技巧。

文件上传的三种实现方式

基础方法:send_keys 操作

这是最直接的上传方式,适用于带有 input 标签的上传控件。当页面元素本质是 <input type="file"> 时,Selenium 可以通过 send_keys 方法直接指定文件路径。

from selenium import webdriver
from time import sleep

driver = webdriver.Chrome()
driver.get("https://example.com/upload")

upload_button = driver.find_element("id", "fileUpload")
upload_button.send_keys("/Users/name/Downloads/testfile.txt")

submit_button = driver.find_element("id", "submit")
submit_button.click()

sleep(5)
driver.quit()

注意路径需要使用绝对路径,且文件类型必须与上传控件支持的格式匹配。这个方法就像快递员直接把包裹交给收件人,省去了中间的搬运过程。

高级方法:模拟拖拽上传

当上传控件被装饰成拖拽样式时,可以使用 ActionChains 模拟真实的拖拽行为。这类场景常见于现代网页设计中。

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
import time

driver = webdriver.Chrome()
driver.get("https://example.com/dragndrop")

drop_area = driver.find_element("id", "dropzone")

actions = ActionChains(driver)
actions.move_to_element(drop_area) \
       .click() \
       .send_keys("/Users/name/Downloads/testfile.txt") \
       .perform()

time.sleep(5)
driver.quit()

这段代码模拟了用户手动拖拽文件到指定区域的行为,就像把快递包裹从门口搬到快递柜的过程。

文件下载路径的灵活配置

设置默认下载目录

通过修改浏览器配置参数,我们可以控制下载文件的保存位置。这个功能特别适合需要批量下载测试文件的场景。

from selenium import webdriver
import os

download_dir = os.path.expanduser("~/Downloads/selenium_downloads")
os.makedirs(download_dir, exist_ok=True)

chrome_options = webdriver.ChromeOptions()
prefs = {"download.default_directory": download_dir}
chrome_options.add_experimental_option("prefs", prefs)

driver = webdriver.Chrome(options=chrome_options)
driver.get("https://example.com/download")

这个设置就像提前在快递柜注册地址,确保收到的包裹自动送到指定位置。不同浏览器的配置参数略有差异,需要查阅对应文档。

监控下载进度

在下载完成后进行断言验证时,需要确保文件已完整下载。可以使用文件系统监控来实现。

import time
import os

def wait_for_download(download_dir, timeout=30):
    start_time = time.time()
    while time.time() - start_time < timeout:
        files = os.listdir(download_dir)
        for file in files:
            if file.endswith(".crdownload"):
                time.sleep(1)
                break
        else:
            return True
    return False

download_dir = os.path.expanduser("~/Downloads/selenium_downloads")
assert wait_for_download(download_dir), "下载未完成"

通过检测临时文件后缀 .crdownload,我们可以像快递员检查包裹状态一样确认下载进度。

实战案例:完整的上传下载流程

案例一:上传验证测试

测试一个典型的文件上传功能,验证上传成功后的提示信息。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome()
driver.get("https://example.com/upload-test")

upload_input = driver.find_element(By.XPATH, "//input[@type='file']")
upload_input.send_keys("/Users/name/Downloads/testfile.txt")

WebDriverWait(driver, 10).until(
    EC.text_to_be_present_in_element((By.ID, "upload-status"), "上传成功")
)
print("上传验证通过")
driver.quit()

这个案例展示了完整的上传流程,包括元素定位、文件传输和结果验证。

案例二:下载验证测试

验证下载链接是否正常工作,并检查文件完整性。

from selenium import webdriver
import time
import hashlib

chrome_options = webdriver.ChromeOptions()
prefs = {"download.default_directory": "/Users/name/Downloads/selenium_downloads"}
chrome_options.add_experimental_option("prefs", prefs)

driver = webdriver.Chrome(options=chrome_options)
driver.get("https://example.com/download-test")
driver.find_element(By.LINK_TEXT, "Download file").click()

time.sleep(5)

downloaded_file = "/Users/name/Downloads/selenium_downloads/testfile.txt"
with open(downloaded_file, "rb") as f:
    file_hash = hashlib.md5(f.read()).hexdigest()
assert file_hash == "e99a18c428cb38d5f260853678922e03", "文件校验失败"
print("下载验证通过")
driver.quit()

通过哈希校验确保下载文件的完整性,就像快递柜扫描包裹编号确认收件。

常见问题解决方案

处理文件选择对话框

在部分系统(如 Windows)上,Selenium 无法直接操作原生文件选择对话框。此时需要借助第三方库如 pyautoguipywinauto

import pyautogui
from selenium import webdriver

driver = webdriver.Chrome()
driver.get("https://example.com/native-upload")

driver.find_element(By.ID, "uploadBtn").click()

pyautogui.write("/Users/name/Downloads/testfile.txt")
pyautogui.press("enter")

driver.quit()

这种方法虽然依赖系统级操作,但能解决原生对话框的兼容性问题。

多文件上传支持

当需要上传多个文件时,send_keys 方法支持通过换行符分隔多个文件路径。

upload_button.send_keys("/Users/name/Downloads/file1.txt\n" +
                      "/Users/name/Downloads/file2.pdf\n" +
                      "/Users/name/Downloads/file3.jpg")

这种操作就像一次性寄送多个包裹,需要确保每个文件路径都正确无误。

跨浏览器兼容性处理

Firefox 的文件上传

Firefox 浏览器需要使用 sendKeys 方法(注意大小写差异)。

// JavaScript 示例
const uploadInput = document.getElementById('fileUpload');
uploadInput.sendKeys('/home/user/testfile.txt');

Safari 的下载限制

Safari 默认禁止自动下载,需要在浏览器设置中启用"自动下载"权限。

safari_options = webdriver.SafariOptions()
safari_options.add_argument("--disable-popup-blocking")
driver = webdriver.Safari(options=safari_options)

不同浏览器的配置方式差异可能会影响测试稳定性,建议在测试环境保持统一浏览器配置。

进阶技巧与最佳实践

动态文件名处理

当测试需要验证下载文件名是否符合预期时,可以使用正则表达式匹配。

import re
import os

files = os.listdir(download_dir)
latest_file = max([os.path.join(download_dir, f) for f in files], 
                 key=os.path.getctime)
assert re.match(r"report_\d{8}_\d{6}\.xlsx", os.path.basename(latest_file)), 
       "文件名格式不符合预期"

这种验证方式就像快递员检查包裹上的日期标签,确保文件时间戳符合业务逻辑。

清理测试数据

测试完成后自动清理生成的文件,避免污染测试环境。

def cleanup_downloads(download_dir):
    for file in os.listdir(download_dir):
        file_path = os.path.join(download_dir, file)
        if os.path.isfile(file_path):
            os.remove(file_path)

cleanup_downloads(download_dir)

这个清理步骤就像收快递后把包装盒整理好,为下次测试腾出空间。

总结与扩展建议

Selenium 文件上传和下载功能覆盖了现代 Web 应用的常见场景,但实际应用中还需要注意以下几点:

  1. 确保测试文件路径在测试环境中可访问
  2. 处理大文件上传时的超时设置
  3. 验证下载文件的大小和内容完整性
  4. 使用浏览器兼容模式处理特殊场景

建议读者结合 unittest 框架编写测试用例,通过参数化测试覆盖不同文件类型和大小的组合。对于更复杂的场景,可以考虑将 Selenium 与 Behave 等 BDD 工具结合使用,提升测试用例的可维护性。

掌握这些核心技巧后,您就能像专业的快递员一样,精准处理 Web 应用中的文件上传和下载操作。建议通过官方文档深入了解不同浏览器的配置参数,积累更多实战经验。