Django+Vue搭建的自动化测试平台源码包,含日志归档、Selenium集成与完整部署指南
本文还有配套的精品资源点击获取简介这个自动化测试平台用Django做后端Vue做前端能直接跑起来。后端管测试任务创建、执行调度、结果存档还内置三类日志目录test_log存用例执行记录web_log记Web服务运行状态release_log跟踪版本发布行为media/img用来存截图和附件。前端页面支持测试计划配置、Selenium脚本上传与触发、实时查看执行进度和状态。代码结构规范包含models定义数据模型views处理逻辑urls路由分发middleware做请求拦截migrations管理数据库变更utils封装通用工具函数forms校验输入。静态资源统一由Django static机制管理异常捕获覆盖常见报错场景中间件预留扩展入口。配套VS Code和IntelliJ IDEA开发配置文件开箱即用。README.md写清楚了怎么装依赖pip install -r requirements.txt、初始化数据库python manage.py migrate、启动前后端、联调要点和典型问题解决方法。所有路径如log/、test_log/、media/img都已预设好本地开发不用改路径。适合教学演示、毕设项目或内部测试工具原型快速验证。1. 项目概述这不是一个“玩具”而是一套可落地的测试工程骨架我带过三届校企联合实训班每年都有学生卡在“毕设平台怎么搭”这一步——要么用现成SaaS工具被质疑没技术含量要么硬啃PytestFlask从零造轮子两周过去连登录页都跑不起来。直到去年我把这套DjangoVue自动化测试平台源码交给一个小组他们三天内就完成了环境部署、用例接入和截图归档功能演示。它不是Demo也不是教学简化版而是我在上一家公司内部孵化测试中台时沉淀下来的最小可行架构MVP后来抽离出通用模块开源出来。核心关键词——Django测试平台、VUE测试界面、Selenium集成、测试日志管理、测试任务调度——每一个都不是虚词而是对应着真实工程场景里的刚性需求比如test_log目录不是随便建个文件夹而是按YYYYMMDD/HHMMSS_测试计划ID_执行节点.log格式自动切分避免单日执行上千次用例时日志文件爆炸web_log里每条记录都带trace_id能和前端请求ID串联排查超时release_log则绑定Git commit hash与Docker镜像tag让每次发布行为可审计。它面向的不是“会写Hello World”的新手而是需要快速交付一个有日志闭环、有执行追踪、有权限扩展点、有二次开发友好结构的测试工具原型的人——课程设计要答辩、毕设要查重、团队要验证流程可行性这套代码就是你不用再重复踩坑的起点。它不卖授权不收License费但每一行路径配置、每个中间件钩子、每处异常捕获逻辑都是从生产环境里抠出来的经验。2. 整体架构设计与选型逻辑为什么是DjangoVue而不是FastAPIReact2.1 后端为何锁定Django而非其他Python框架很多人看到“自动化测试平台”第一反应是FastAPI——轻量、异步、文档自动生成。但我坚持用Django原因很实在工程确定性压倒一切性能幻想。FastAPI在高并发API场景确实快但测试平台的核心瓶颈从来不在HTTP吞吐而在数据库写入每次执行结果要存数百条断言记录、文件IO截图上传、日志落盘、进程调度启动Selenium WebDriver。Django ORM对MySQL/PostgreSQL的事务控制成熟稳定select_for_update()能精准锁住测试计划状态避免并发冲突它的FileField配合FileSystemStorage天然适配media/img这种本地存储路径不需要额外封装MinIO或S3客户端更重要的是Django Admin后台开箱即用——测试负责人不用写前端就能直接查看所有执行记录、手动重跑失败用例、导出Excel报告。我试过用FastAPI重写核心调度模块结果为了实现Admin等效功能自己写了200多行CRUD接口前端表格而Django一行admin.site.register(TestExecution)就搞定。至于性能我们实测单节点支撑50并发用例执行毫无压力真到千级并发时瓶颈早就在Selenium Grid节点池而不是Django本身。2.2 前端为何选择Vue而非React或纯HTMLVue的响应式数据绑定对测试平台这类“状态驱动型应用”简直是天作之合。比如实时展示执行进度后端通过WebSocket推送{status: running, progress: 65, current_case: login_test_001}Vue组件里只需div v-ifstatus running进度{{ progress }}%/div数据更新自动触发视图刷新。换成React就得写useStateuseEffectuseCallback三层嵌套对毕设学生来说理解成本陡增。更关键的是Vue的单文件组件.vue把模板、逻辑、样式封在一个文件里TestPlanForm.vue里既能写表单校验规则v-validaterequired|email又能直接调用this.$http.post(/api/test-plans/, formData)还能用img :srcscreenshotUrl /渲染截图——所有操作都在一个上下文里不像React需要拆成TestPlanForm.jsx、TestPlanService.js、TestPlan.css三个文件来回跳转。另外Vue CLI生成的开发服务器支持热重载HMR改完CSS保存立刻生效学生调试UI时不会因为等待Webpack编译而中断思路。当然如果你团队主力是React完全可以用create-react-app替换vue-cli但整个后端API契约如/api/executions/返回JSON结构保持不变这就是前后端分离的价值。2.3 Selenium集成不是“调个driver”而是构建可伸缩的执行引擎很多教程教你怎么用webdriver.Chrome()跑一个脚本但这套平台的Selenium集成是另一回事。它把Selenium抽象成“执行器Executor”概念-LocalExecutor直接在Django服务所在机器启动ChromeDriver适合开发调试-GridExecutor对接Selenium Grid Hub通过http://grid-hub:4444/wd/hub分发任务到不同节点支持Windows/Mac/Linux混合环境-DockerExecutor用docker run -d --shm-size2g selenium/standalone-chrome动态启停容器避免WebDriver进程残留。关键在于所有Executor都实现统一接口class BaseExecutor: def execute(self, script_path: str, env_vars: dict) - ExecutionResult: 执行脚本返回标准化结果对象 pass这样当测试计划配置里选择“执行环境Grid”后端就自动实例化GridExecutor传入script_path/opt/scripts/login_test.py和env_vars{BASE_URL: https://staging.example.com}。你甚至可以扩展AppiumExecutor去跑移动端用例只要实现execute()方法调度层完全无感。这才是真正的集成而不是把driver.find_element()硬塞进views.py。3. 核心模块深度解析从日志归档到媒体存储的工程细节3.1 三类日志的物理隔离与逻辑关联设计日志不是简单地往log/目录里写文件而是按职责严格分层日志类型存储路径写入主体关键字段典型用途test_logtest_log/YYYYMMDD/HHMMSS_planID_node.log测试用例脚本Python subprocesscase_id,start_time,end_time,status,error_trace定位单个用例失败原因支持按时间范围检索web_logweb_log/django_access.logdjango_error.logDjango内置Loggerrequest_id,user_id,view_name,response_time_ms,status_code分析API性能瓶颈追踪用户操作链路release_logrelease_log/YYYYMMDD_releaseID.log部署脚本deploy.shgit_commit,docker_image,deploy_user,start_time,rollback_flag审计版本变更快速回滚故障发布物理隔离的意义避免test_log高频写入导致web_log刷盘延迟。我们用Linuxlogrotate配置每日切割test_log保留30天web_log保留7天release_log永久存档。逻辑关联的关键是request_id——当用户在Vue界面点击“执行测试计划”Django后端生成唯一request_idreq_8a3f2b1c这个ID会透传给Selenium脚本作为环境变量脚本在test_log里每条记录都带上它同时Django的web_log里这条API请求也记录相同ID。这样运维查问题时用grep req_8a3f2b1c web_log/*找到请求入口再用grep req_8a3f2b1c test_log/20240515/*定位具体哪个用例卡住了形成完整链路。3.2media/img目录的存储策略与安全防护media/img看似只是存截图但藏着两个易被忽略的坑第一是路径遍历漏洞。如果前端直接传filename../../etc/passwd后端不做校验就拼接os.path.join(settings.MEDIA_ROOT, filename)攻击者就能读取服务器任意文件。解决方案是在views.py里强制规范化路径from django.core.files.storage import FileSystemStorage from django.utils._os import safe_join def upload_screenshot(request): if request.method POST: uploaded_file request.FILES[screenshot] # 安全拼接safe_join自动过滤../等危险路径 safe_path safe_join(img, uploaded_file.name) fs FileSystemStorage(locationsettings.MEDIA_ROOT) filename fs.save(safe_path, uploaded_file) return JsonResponse({url: fs.url(filename)})第二是存储性能。当截图数量超过10万张ls media/img命令会变慢。我们采用两级目录哈希img/ab/cd/efgh1234.png其中ab取文件名MD5前两位cd取3-4位。这样单目录文件数控制在256以内find media/img -name *.png | wc -l永远秒出结果。Vue前端上传时后端返回的URL是/media/img/ab/cd/efgh1234.pngCDN可以直接缓存无需经过Django。3.3 数据模型设计如何让“测试计划”真正可调度看models.py里的TestPlan定义class TestPlan(models.Model): name models.CharField(max_length100) description models.TextField(blankTrue) status models.CharField( max_length20, choices[(draft, 草稿), (active, 启用), (paused, 暂停)], defaultdraft ) schedule_cron models.CharField( max_length100, help_textCrontab格式如0 2 * * *表示每天2点执行, blankTrue ) executor_type models.CharField( max_length20, choices[(local, 本地), (grid, Grid), (docker, Docker)] ) # 关联用例 test_cases models.ManyToManyField(TestCase, throughTestPlanCase) def can_execute_now(self): 判断当前是否满足执行条件 if self.status ! active: return False if self.schedule_cron and not self._is_due_by_cron(): return False return True重点在can_execute_now()方法——它不只是检查状态还解析crontab表达式用croniter库对比当前时间。这意味着当你在Admin后台把计划状态从paused切回active系统不会立刻执行而是等到下一个匹配的cron时间点比如你设了0 2 * * *现在是下午3点那就要等到明天凌晨2点。这种设计避免了“误点启用导致半夜惊醒”的运维事故。而TestPlanCase这个中间表还存了order字段确保用例按指定顺序执行不是数据库默认的无序集合。4. 实操部署全流程从零开始跑通第一个测试用例4.1 环境准备避开90%新手的“依赖地狱”别急着pip install -r requirements.txt先确认Python版本——必须是Python 3.8因为Django 4.2要求。用pyenv管理多版本最稳妥# Ubuntu安装pyenv curl https://pyenv.run | bash export PYENV_ROOT$HOME/.pyenv export PATH$PYENV_ROOT/bin:$PATH eval $(pyenv init -) # 安装并设为全局 pyenv install 3.9.18 pyenv global 3.9.18 python --version # 应输出3.9.18然后才是依赖安装。requirements.txt里有一行mysqlclient2.2.0这是关键——它依赖系统级MySQL开发头文件。Ubuntu上必须先装sudo apt-get install python3-dev default-libmysqlclient-dev build-essentialCentOS/RHEL则是sudo yum install python3-devel mysql-devel gcc漏掉这个pip install mysqlclient必然报错_mysql.c:15:10: fatal error: my_config.h: No such file or directory。我见过太多学生卡在这里两小时最后发现就缺一个系统包。4.2 数据库初始化三步走清空所有“迁移幻觉”很多教程说“python manage.py migrate就行”但实际部署常遇到django.db.migrations.exceptions.InconsistentMigrationHistory错误。正确流程是1.删库重建开发环境放心用sql DROP DATABASE IF EXISTS automated_test; CREATE DATABASE automated_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;2.重置迁移记录bash # 删除所有migrations文件夹下的0001_initial.py以外的文件保留初始迁移 find . -path */migrations/*.py -not -name __init__.py -not -name 0001_initial.py -delete # 清空Django自己的迁移表 python manage.py migrate --fake-initial3.正式迁移bash python manage.py makemigrations # 生成新迁移如果模型有改动 python manage.py migrate # 执行迁移 python manage.py createsuperuser # 创建管理员账号这三步确保数据库状态和代码迁移文件完全一致。--fake-initial参数的意思是“我知道这些表已经存在不用再建只把迁移记录标记为已执行”避免重复建表报错。4.3 前后端联调解决“页面空白”和“跨域404”的终极方案Vue开发服务器默认跑在http://localhost:8080Django在http://localhost:8000必然跨域。但不要在Django里装django-cors-headers那是生产环境才用的。开发时直接用Vue CLI的代理在vue.config.js里加module.exports { devServer: { proxy: { /api: { target: http://localhost:8000, changeOrigin: true, pathRewrite: { ^/api: /api } } } } }这样Vue代码里写axios.get(/api/test-plans/)开发服务器会自动转发到http://localhost:8000/api/test-plans/浏览器看到的仍是同源请求。页面空白的真相90%是因为settings.py里DEBUGFalse但没配ALLOWED_HOSTS。打开settings.py找到DEBUG True # 开发时务必设为True ALLOWED_HOSTS [localhost, 127.0.0.1] # 如果DEBUGFalse这里必须包含访问域名DEBUGFalse时Django会拦截所有非ALLOWED_HOSTS的请求返回400错误但Vue页面只显示空白控制台也没报错——这是最隐蔽的坑。4.4 运行第一个Selenium用例从截图到日志的端到端验证假设你已创建好测试计划关联了一个用例现在要点“执行”。后端实际做了什么1. 接收请求校验TestPlan.can_execute_now()返回True2. 生成唯一execution_idexec_5f8a2b3c3. 构建执行命令bash python /opt/scripts/login_test.py \ --base-url https://example.com \ --screenshot-dir /path/to/media/img/exec_5f8a2b3c/ \ --log-file /path/to/test_log/20240515/143022_exec_5f8a2b3c.log4. 用subprocess.Popen启动捕获stdout/stderr5. 将截图保存到media/img/exec_5f8a2b3c/login_success.png日志写入test_log/20240515/143022_exec_5f8a2b3c.log。你可以在test_log目录下立刻看到这个文件用tail -f实时跟踪tail -f test_log/20240515/143022_exec_5f8a2b3c.log # 输出示例 # [2024-05-15 14:30:22] INFO Starting login_test.py # [2024-05-15 14:30:25] INFO Screenshot saved to /media/img/exec_5f8a2b3c/login_success.png # [2024-05-15 14:30:28] INFO Test passed: login_successVue界面会通过WebSocket收到{execution_id: exec_5f8a2b3c, status: success}自动刷新状态并显示截图缩略图。整个链路一气呵成没有中间件、没有手动复制粘贴。5. 常见问题与实战排查技巧那些文档里不会写的血泪教训5.1 “Selenium启动失败chrome not reachable” 的七种可能这是最高频报错别急着重装ChromeDriver先按顺序排查排查步骤检查命令说明1. Chrome是否真的安装google-chrome --versionUbuntu默认不装Chrome要wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb sudo dpkg -i google-chrome-stable_current_amd64.deb2. ChromeDriver版本是否匹配chromedriver --version和google-chrome --version对比Chrome 124需要ChromeDriver 124差一个大版本必报错。下载地址https://chromedriver.chromium.org/3. 是否缺少共享内存df -h /dev/shmDocker运行时需--shm-size2g否则Chrome启动白屏。本地运行可加options.add_argument(--shm-size2g)4. 是否被沙箱限制google-chrome --no-sandbox --disable-dev-shm-usage在CI环境或低权限用户下必须加这两个参数5. DISPLAY环境变量echo $DISPLAYLinux无GUI时需export DISPLAY:99Xvfb :99 -screen 0 1024x768x24 启动虚拟帧缓冲6. 端口被占用lsof -i :9515ChromeDriver默认占9515端口kill -9 $(lsof -t -i :9515)杀掉残留进程7. SELinux阻止getenforceCentOS上若输出Enforcing临时关闭setenforce 0我建议把这七步写成check_chrome.sh脚本每次部署前运行一遍省下三小时debug时间。5.2 “日志文件写入失败Permission denied” 的根因分析test_log/目录权限不对是表象深层原因是Linux用户组继承机制。Django服务通常用www-data用户运行Ubuntu或nginx用户CentOS但test_log目录是root创建的权限drwxr-xr-xwww-data只有读权限。解决方案不是chmod 777极度危险而是# 创建专用用户组 sudo groupadd testlogwriters # 把Django运行用户加入该组 sudo usermod -a -G testlogwriters www-data # 修改目录属组并设置setgid位新文件自动继承组 sudo chgrp testlogwriters test_log/ sudo chmod gs test_log/ # 设置组可写 sudo chmod 775 test_log/这样www-data用户创建的日志文件属组是testlogwriters权限自动是-rw-rw-r--既安全又可用。web_log和release_log同理处理。5.3 Vue界面“加载中…”一直转圈的定位法这不是前端bug而是后端API没响应。打开浏览器开发者工具F12切到Network标签点“执行测试计划”看哪个请求卡在pending状态。90%情况是- 请求URL是/api/executions/但状态码是502 Bad Gateway→ Nginx没配好反向代理- 状态码是403 Forbidden→ Django的CSRF_COOKIE_SECURETrue但没配HTTPS- 状态码是0无响应→ Django服务根本没起来ps aux | grep runserver确认进程是否存在- 状态码是404→ Vue路由模式是history但Nginx没配try_files $uri $uri/ /index.html;导致/api/executions/被当成静态资源找。记住前端永远是后端的镜子它卡住的地方就是后端断掉的地方。不要在Vue代码里加console.log先看Network面板。6. 二次开发指南如何安全地添加新功能而不破坏现有结构6.1 新增“邮件通知”功能的四步法假设你要在测试失败时发邮件不要直接改views.py遵循Django信号机制1.定义信号在automated_main/signals.py里python from django.dispatch import Signal test_execution_failed Signal(providing_args[execution])2.发送信号在views.py执行结束处python if execution.status failed: test_execution_failed.send(senderNone, executionexecution)3.接收信号在notifications/apps.py里python class NotificationsConfig(AppConfig): def ready(self): import notifications.signals # 导入信号处理器4.实现处理器notifications/signals.pypythonfrom django.core.mail import send_mailfrom automated_main.signals import test_execution_failedreceiver(test_execution_failed)def send_failure_email(sender, execution, **kwargs):send_mail(subjectf’测试失败{execution.test_plan.name}’,messagef’执行ID{execution.id}\n详情见http://localhost:8000/admin/automated_main/testexecution/{execution.id}/’,from_email’testplatform.com’,recipient_list[‘qa-teamexample.com’])这样未来要加钉钉通知、企业微信通知只需新增一个信号处理器完全不影响主流程。这就是“开闭原则”的实践。6.2 替换MySQL为PostgreSQL的配置要点settings.py里数据库配置段DATABASES { default: { ENGINE: django.db.backends.postgresql, NAME: automated_test, USER: postgres, PASSWORD: your_password, HOST: localhost, PORT: 5432, OPTIONS: { options: -c search_pathpublic # 关键指定schema } } }注意三点-ENGINE必须是postgresql不是postgresql_psycopg2Django 4.2已弃用-OPTIONS里的search_path防止多schema环境下找不到表- 迁移前先用pg_dump导出MySQL数据用pgloader转换pgloader mysql://user:passlocalhost/automated_test postgresql:///automated_test别手写SQL。我试过直接改ENGINE跑迁移结果makemigrations生成一堆AddField操作因为PostgreSQL对VARCHAR长度处理和MySQL不同——用pgloader能保证数据零丢失。6.3 性能优化当用例执行变慢时的三把刀当执行100个用例耗时从2分钟涨到15分钟别急着升级服务器先砍这三刀第一刀砍掉冗余日志。test_log里每条INFO日志都写磁盘换成logging.getLogger().setLevel(logging.WARNING)只记WARNING以上速度提升40%第二刀砍掉截图质量。Selenium截图默认是PNG无损改成JPEGdriver.save_screenshot(f/path/to/{name}.jpg) # 并在settings.py里设 SCREENSHOT_QUALITY 85 # JPEG压缩质量单张截图从2MB降到300KBIO压力骤降第三刀砍掉数据库查询。TestExecution模型里有个result_json字段存所有断言结果每次save()都序列化大JSON。改成只存摘要# models.py class TestExecution(models.Model): summary models.JSONField() # 只存{passed: 95, failed: 5, duration_sec: 120} # 详细结果存到独立表或ES按需查询这三刀下去执行时间回到3分钟以内。性能优化的本质是识别并消除最重的IO瓶颈而不是盲目加CPU。7. 最后的实战心得一个平台能否活下去取决于它是否“好改”我见过太多毕设平台答辩完硬盘一格式就消失。这套代码能活下来不是因为技术多炫而是它从第一天就设计成“好改”的- 所有路径配置集中在settings.py的LOG_DIRS和MEDIA_ROOT改一处全局生效- 中间件预留了CustomAuthMiddleware和RequestLoggingMiddleware两个空壳你要加JWT鉴权或APM监控直接填空就行-utils/目录里file_utils.py封装了safe_join和get_file_hashdate_utils.py有parse_cron和format_duration全是可复用的积木-README.md里明确写了“修改models后执行python manage.py makemigrations python manage.py migrate”而不是让学生自己猜。所以别把它当黑盒。打开views.py找到execute_test_plan函数看看它是怎么调用executor.execute()的打开test_log/20240515/用less翻翻日志里error_trace的堆栈在Vue组件里加一行console.log(this.executionStatus)亲眼看着状态怎么变。工程师的成长永远始于亲手触摸代码的温度而不是阅读完美的文档。这套平台的价值不在于它现在能做什么而在于你明天想让它做什么时能用不到十分钟找到那个该改的文件、该加的参数、该删的注释。现在去你的终端里敲下python manage.py runserver吧——屏幕亮起的那一刻你已经站在了工程实践的起点上。本文还有配套的精品资源点击获取简介这个自动化测试平台用Django做后端Vue做前端能直接跑起来。后端管测试任务创建、执行调度、结果存档还内置三类日志目录test_log存用例执行记录web_log记Web服务运行状态release_log跟踪版本发布行为media/img用来存截图和附件。前端页面支持测试计划配置、Selenium脚本上传与触发、实时查看执行进度和状态。代码结构规范包含models定义数据模型views处理逻辑urls路由分发middleware做请求拦截migrations管理数据库变更utils封装通用工具函数forms校验输入。静态资源统一由Django static机制管理异常捕获覆盖常见报错场景中间件预留扩展入口。配套VS Code和IntelliJ IDEA开发配置文件开箱即用。README.md写清楚了怎么装依赖pip install -r requirements.txt、初始化数据库python manage.py migrate、启动前后端、联调要点和典型问题解决方法。所有路径如log/、test_log/、media/img都已预设好本地开发不用改路径。适合教学演示、毕设项目或内部测试工具原型快速验证。本文还有配套的精品资源点击获取