作业所属课程 | https://edu.cnblogs.com/campus/fzu/SE2024/ |
---|---|
作业要求 | https://edu.cnblogs.com/campus/fzu/SE2024/homework/13310 |
作业的目标 | beta冲刺总结 |
团队名称 | 银河护胃队 |
团队成员学号-名字 | 072208130-曹星才(组长) 052205144-张诗悦 102201120-陈康培 102201342-潘宇晴 102202108-王露洁 102202111-刘哲睿 102202128-林子豪 102202142-黄悦佳 102202149-詹镇壕 102202153-来再提·叶鲁别克 |
一、alpha冲刺后问题与解决
问题一
问题:
在小程序的部署过程中,我们只能在安卓手机端上运行,其他设备如苹果、电脑,暂时无法使用。
解决方法:
iOS设备调试:对于iOS设备,需要在开发者工具中设置特殊UserAgent让小程序代码知道是iOS平台,并且在iOS设备上拦截URL特殊处理,以便小程序代码在iOS平台上正常运行。
问题二
问题:
前后端对接未完成,部分后端接口设计在同一个端口,且flask默认是单线程的,当接受较多请求时,后端响应时速度过慢
探索思路:
可以通过提高硬件性能解决,但不现实,考虑能否实现多线程同步响应或者将部分接口调用端口分离,以提高响应速度。
解决过程:
对不同接口进行分离调用,如将大模型部分的接口与数据库数据管理的接口分别部署在不同的端口上,同时使用flask的并发接口,开启多线程处理请求,提高处理速度。
问题三
问题:
部分页面或组件在不同设备上显示不正常,可能是由于响应式设计不足或样式冲突,导致页面在不同尺寸的屏幕上无法自适应。
解决方案:
使用浏览器的开发者工具模拟不同设备,检查布局和样式,发现部分页面未使用合适的媒体查询来适配不同的屏幕尺寸。针对这个问题,增加了媒体查询来确保页面在各种设备上都能自适应显示,同时使用了 flexbox 和 grid 等 CSS 布局模块来优化布局的响应式效果,确保字体、图像等元素在各种屏幕上都能正确显示。
问题四
问题:
在前端开发中,遇到了一个影响用户体验的问题:食谱详情页面加载缓慢,尤其是在网络环境较差的情况下,用户需要等待较长时间才能看到食谱内容。
解决方案:
使用浏览器的开发者工具进行网络分析,发现每次加载食谱详情时,页面会发起多个 API 请求,而部分 API 请求的响应时间较长,尤其是获取食材的接口。所以进行了对API 请求的合并,即将多个相关的 API 请求合并为一个请求,减少网络请求次数。
问题五
问题:
在协助前端进行整合时,前端的代码无法调用后端的接口。
解决方案:
为了解决前端代码无法调用后端接口的问题,我们采取了以下措施:首先,我们在后端服务器上配置了CORS策略,允许前端域名进行跨域请求。但没用,后面我们研究后发现在服务器上的开放的端口太少,太多请求同时进行会占用端口导致无法调用。为了解决这个问题,我们在服务器上多开放了几个端口。这样可以确保即使在高并发的情况下,服务器也能够处理来自前端的请求,避免因端口不足而导致的调用失败。通过这些措施,我们成功解决了前端代码无法调用后端接口的问题,确保了项目的顺利进行。
问题六
问题:
前端和后端之间的数据传输格式(JSON、表单数据等)会出现问题。例如,前端发送的数据结构不符合后端的要求,或者后端返回的数据格式不符合前端的预期。前端和后端可能部署在不同的域上,这会导致跨域问题,浏览器阻止前端请求后端接口。
解决办法:
确保前后端的数据格式统一,使用 JSON 作为标准数据交换格式;前端发送请求时需要设置正确的 Content-Type,后端则要正确解析请求体中的 JSON 数据。
跨域问题(CORS)。使用 CORS(跨域资源共享)中间件解决跨域问题。在 Flask 中,可以通过安装 flask-cors 来轻松解决
问题七
问题:
调用AI接口并给其模板并合成成为一个大接口并进行调试输出时,返回的答案会是两次一摸一样的
解决方案:
为了解决这个问题,我在每次调用AI接口时增加了一个唯一的调用标识,时间戳以确保每次调用都是独立且可区分的。我仔细审查了接口的代码逻辑,确保没有错误导致重复调用或返回相同的结果。后面发现其实是AI接口输出了一次结果,后面又返回输出了一次。所以我们在处理接口返回数据时,增加了逻辑判断,只取第一次的返回结果进行输出,忽略后续的重复返回。这样就解决了问题
问题八
问题:
在前端页面加入logo背景图片会导致部分按钮失灵。
解决方案:
背景图片使用 position: absolute; 定位,这样它就不会占据任何文档流的空间,从而不会影响按钮的布局和点击区域。通过设置 z-index: 0,确保背景图片位于页面的最底层,这样其他元素(如按钮)就可以位于背景图片之上。背景图片的 opacity 设置为 0.1;,这意味着它是完全不透明的,这样就不会遮挡任何位于其上的元素。
问题九
问题:
关于体重记录的部分,每次记录完之后,记录的新体重就会取代初始体重,而且折线图上每次退出后重新进入也只会保留上次最新的一个记录,导致不好看出体重的变化趋势。
解决思路:
这涉及到后端返回的数据的格式以及数据库中表格的设计,前端需要的是用户体重的所有历史数据,所以相对应的后端从数据库中获取的体重数据应该是json类型,而不是目前的int类型,只要修改一下数据库表格的结构和接口中返回数据的逻辑,应该就能使得前端呈现出体重记录的变化
二、项目特色功能
食谱食物查询
可以查询有什么食谱推荐,点进去可以看到该食谱的详细数据,包括但不限于该食谱的餐厅位置、价格、营养成分、食材、过敏原。
还可以查询食物的数据
在食物红黑榜处可以看对应疾病、或者是减肥、增肌目标的推荐和谨慎食用的食物
随心配
不知道吃什么?可以到我们的随心配界面,“换一换”,挑选你心动的食物!
身体数据记录
身体数据记录,用于后面的健康计划生成和食物推荐
记录每天摄入的食物热量以及体重变化和一些其它的身体数据
AI小助手
普通询问
基于身体数据询问
生成一周计划
生成(保存)为健康计划
三、软件测试
后端测试
API测试
进行多线程测试(10轮次,10线程同时进行)
线程数量为10,此时响应速度较慢,平均请求耗时达到163毫秒,约为单线程次数的三倍
线程数量为20时,达到约260毫秒
Python代码测试
测试用户登录注册等功能
测试通过
前端测试
1. 单元测试(Unit Testing) 单元测试主要用于测试代码中每个独立功能的正确性。在本项目中,前端的每个组件都可以进行单元测试。
-
目标:确保各个组件的功能符合预期,特别是:
collectionItems
是否正确传递到组件并渲染。- 按钮是否按预期执行
goBack
方法,跳转到其他页面。 syncCollection
方法是否正确同步数据(尽管现在是占位方法,未来会实现从云端获取数据的功能)。
-
工具:Jest、Mocha等
-
测试案例:
- 验证
collectionItems
的数据是否在页面中正确渲染。 - 验证返回按钮是否触发页面跳转。
代码:
import { mount } from '@vue/test-utils'; import CollectionPage from '@/components/CollectionPage.vue'; // 假设你的组件路径是这样 import { createRouter, createWebHistory } from 'vue-router'; describe('CollectionPage.vue', () => { let wrapper; const mockPush = jest.fn(); // 模拟router.push方法 beforeEach(() => { // 创建一个mock路由实例 const router = createRouter({ history: createWebHistory(), routes: [ { path: '/home', name: 'home' }, ], }); // 挂载组件 wrapper = mount(CollectionPage, { global: { plugins: [router], }, data() { return { collectionItems: [ { name: '水煮鸡胸肉', description: '低脂高蛋白的健身餐', image: '/path/to/image1.png' }, { name: '水煮虾', description: '低卡高蛋白的健康食物', image: '/path/to/image2.png' }, ], }; }, }); }); it('renders collection items correctly', async () => { // 测试菜品列表是否正确渲染 const items = wrapper.findAll('.collection-item'); expect(items.length).toBe(2); // 确保渲染了两项菜品 const firstItemTitle = items[0].find('.item-title'); expect(firstItemTitle.text()).toBe('水煮鸡胸肉'); // 检查第一个菜品的标题 const secondItemDescription = items[1].find('.item-description'); expect(secondItemDescription.text()).toBe('低卡高蛋白的健康食物'); // 检查第二个菜品的描述 }); it('fires goBack method correctly', async () => { // 测试返回按钮是否触发跳转 const backButton = wrapper.find('.back-button'); await backButton.trigger('click'); // 触发点击事件 expect(mockPush).toHaveBeenCalledWith('/home'); // 验证是否调用了路由跳转方法 }); it('handles empty collectionItems gracefully', async () => { // 测试如果 collectionItems 为空,页面是否没有崩溃 await wrapper.setData({ collectionItems: [] }); const items = wrapper.findAll('.collection-item'); expect(items.length).toBe(0); // 确保没有渲染任何菜品项 }); it('renders correct alt text for images', async () => { // 测试图片的alt属性是否正确 const images = wrapper.findAll('.item-image'); expect(images.at(0).attributes('alt')).toBe('加载中'); // 检查第一个图片的alt文本 }); });
- 验证
2. 验收测试(Acceptance Testing) 验收测试主要验证用户需求是否被满足。通过模拟用户行为,确保应用能够正确实现预期的业务需求。
-
目标:确保用户在“我的收藏”页面的各项操作都能顺利完成:
- 用户点击返回按钮后是否跳转到主页。
- 用户能看到菜品的图片、名称和描述,并且图片能正确加载。
-
工具:Selenium、Cypress等
-
测试案例:
- 用户进入页面时是否显示菜品列表。
- 用户点击返回按钮是否跳转到指定页面。
-
代码:
import { mount } from '@vue/test-utils'; import HealthReportPage from '@/components/HealthReportPage.vue'; // 假设你的组件路径是这样 import { createRouter, createWebHistory } from 'vue-router'; describe('HealthReportPage.vue', () => { let wrapper; const mockPush = jest.fn(); // 模拟router.push方法 beforeEach(() => { // 创建一个mock路由实例 const router = createRouter({ history: createWebHistory(), routes: [ { path: '/home', name: 'home' }, ], }); // 挂载组件 wrapper = mount(HealthReportPage, { global: { plugins: [router], }, data() { return { healthReport: { healthStatus: '无慢性疾病,无过敏现象', lifestyle: '轻度运动,长期久坐', testResults: { bloodSugar: '正常', bloodFat: '正常', }, otherIndicators: '正常', }, }; }, }); }); it('renders health report data correctly', async () => { // 测试健康报告数据是否渲染 const healthStatus = wrapper.find('.health-status'); expect(healthStatus.text()).toBe('健康状况:无慢性疾病,无过敏现象'); const lifestyle = wrapper.find('.lifestyle'); expect(lifestyle.text()).toBe('生活习惯:轻度运动,长期久坐'); const bloodSugar = wrapper.find('.blood-sugar'); expect(bloodSugar.text()).toBe('血糖水平:正常'); const bloodFat = wrapper.find('.blood-fat'); expect(bloodFat.text()).toBe('血脂水平:正常'); const otherIndicators = wrapper.find('.other-indicators'); expect(otherIndicators.text()).toBe('其他指标:正常'); }); it('fires goBack method correctly', async () => { // 测试返回按钮是否触发跳转 const backButton = wrapper.find('.back-button'); await backButton.trigger('click'); // 触发点击事件 expect(mockPush).toHaveBeenCalledWith('/home'); // 验证是否调用了路由跳转方法 }); it('displays health report sections correctly', async () => { // 测试报告页面的不同部分是否显示正确 const reportSections = wrapper.findAll('.report-section'); expect(reportSections.length).toBe(4); // 确保有4个报告部分显示 const reportTitle = wrapper.find('h1'); expect(reportTitle.text()).toBe('体检报告'); // 检查报告标题是否正确 }); it('handles missing data gracefully', async () => { // 测试如果缺少健康报告数据,页面是否正常显示 await wrapper.setData({ healthReport: { healthStatus: '', lifestyle: '', testResults: { bloodSugar: '', bloodFat: '', }, otherIndicators: '', }, }); const healthStatus = wrapper.find('.health-status'); expect(healthStatus.text()).toBe('健康状况:无数据'); // 如果没有数据,应该显示“无数据” }); });
3. 可访问性测试(Accessibility Testing) 可访问性测试确保页面可以被所有用户使用,包括有视力或听力障碍的用户。
-
目标:确保页面符合WCAG(Web Content Accessibility Guidelines)标准。
- 页面元素是否可以通过键盘导航。
- 视力受限的用户是否可以通过屏幕阅读器理解页面内容。
-
工具:axe-core、Google Lighthouse等
-
测试案例
- 检查所有图片是否有合适的
alt
属性。 - 确保页面能够通过键盘进行导航。
- 检查所有图片是否有合适的
-
代码:
import { mount } from '@vue/test-utils'; import HealthReportPage from '@/components/HealthReportPage.vue'; // 假设你的组件路径是这样 import { createRouter, createWebHistory } from 'vue-router'; import { axe } from 'jest-axe'; describe('HealthReportPage.vue', () => { let wrapper; beforeEach(() => { const router = createRouter({ history: createWebHistory(), routes: [ { path: '/home', name: 'home' }, ], }); wrapper = mount(HealthReportPage, { global: { plugins: [router], }, data() { return { healthReport: { healthStatus: '无慢性疾病,无过敏现象', lifestyle: '轻度运动,长期久坐', testResults: { bloodSugar: '正常', bloodFat: '正常', }, otherIndicators: '正常', }, }; }, }); }); it('should have no accessibility violations', async () => { // 使用 axe-core 来检查页面的可访问性 const results = await axe(wrapper.element); // 验证没有可访问性问题 expect(results).toHaveNoViolations(); }); it('should have proper ARIA attributes', async () => { // 测试健康报告页面是否包含必要的ARIA属性 const healthStatus = wrapper.find('.health-status'); expect(healthStatus.attributes('aria-label')).toBe('健康状况'); // 确保有正确的ARIA标签 const lifestyle = wrapper.find('.lifestyle'); expect(lifestyle.attributes('aria-label')).toBe('生活习惯'); // 确保有正确的ARIA标签 }); it('should have proper tab order', async () => { // 测试页面上的可交互元素(如按钮、输入框等)是否有合适的tab顺序 const focusableElements = wrapper.findAll('button, a, input, select, textarea'); const tabIndexes = focusableElements.map((el) => el.element.tabIndex); // 确保tab顺序是正确的 expect(tabIndexes).toEqual([0, 0, 0]); // 根据你的页面内容调整预期的tabIndex值 }); it('should have proper alt text for images', async () => { // 测试页面中的所有图片是否有合适的alt文本 const images = wrapper.findAll('img'); images.wrappers.forEach((imageWrapper) => { const altText = imageWrapper.attributes('alt'); expect(altText).not.toBe(''); }); }); });
4. 回归测试(Regression Testing) 回归测试确保新修改或新增的代码不会破坏现有功能。
-
目标:确保在代码更新后,页面的各个功能依然能够正常运行。
- 在修改代码后,重新运行所有测试用例。
-
工具:Jest、Cypress等
-
测试案例
- 运行全部单元测试,确保组件功能不被破坏。
- 测试各个页面的跳转是否正常,确保返回按钮和其他功能无误。
-
代码:
import { mount } from '@vue/test-utils'; import HealthReportPage from '@/components/HealthReportPage.vue'; // 假设你的组件路径是这样 import { createRouter, createWebHistory } from 'vue-router'; describe('HealthReportPage.vue', () => { let wrapper; beforeEach(() => { const router = createRouter({ history: createWebHistory(), routes: [ { path: '/home', name: 'home' }, ], }); wrapper = mount(HealthReportPage, { global: { plugins: [router], }, data() { return { healthReport: { healthStatus: '无慢性疾病,无过敏现象', lifestyle: '轻度运动,长期久坐', testResults: { bloodSugar: '正常', bloodFat: '正常', }, otherIndicators: '正常', }, }; }, }); }); it('should display correct health status', () => { const healthStatus = wrapper.find('.health-status'); expect(healthStatus.text()).toBe('无慢性疾病,无过敏现象'); }); it('should display correct lifestyle habits', () => { const lifestyle = wrapper.find('.lifestyle'); expect(lifestyle.text()).toBe('轻度运动,长期久坐'); }); it('should display correct test results', () => { const bloodSugar = wrapper.find('.blood-sugar'); expect(bloodSugar.text()).toBe('血糖水平正常'); const bloodFat = wrapper.find('.blood-fat'); expect(bloodFat.text()).toBe('血脂水平正常'); }); it('should display correct other indicators', () => { const otherIndicators = wrapper.find('.other-indicators'); expect(otherIndicators.text()).toBe('正常'); }); it('should handle changes in health status', async () => { // 模拟更新健康状态 await wrapper.setData({ healthReport: { healthStatus: '有高血压', lifestyle: '轻度运动,长期久坐', testResults: { bloodSugar: '正常', bloodFat: '正常', }, otherIndicators: '正常', }, }); const healthStatus = wrapper.find('.health-status'); expect(healthStatus.text()).toBe('有高血压'); }); });