2024秋软件工程结对作业(第二次之编码实现)报告
2024秋软件工程结对作业(第二次之编码实现)报告
项目信息
项目信息 | 数据 |
---|---|
这个作业要求在哪里 | 作业要求 |
这个作业的目标 | 实现跨专业组队微信小程序 |
姓名及学号 | 102201118 陆旭东 |
结对成员及学号 | 102201120 陈康培 |
结对同学博客链接 | 陆旭东的博客 |
GitHub项目地址 | 陆旭东同学的Github仓库 - 102201118-102201120 |
分工
- 陈康培:负责前端页面开发、小程序逻辑编写、结对作业报告的撰写。
- 陆旭东:负责后端接口开发、数据库设计、结对作业报告的撰写。
PSP表格
PSP2.1 Stages | 预估耗时(小时) | 实际耗时(小时) | 备注 |
---|---|---|---|
Planning | 2 | 3 | 需求分析更复杂 |
Development | 15 | 15 | 开发中遇到技术难题 |
Analysis | 5 | 6 | 学习新技术 |
Design Spec | 3 | 4 | 设计复杂度高 |
Design Review | 2 | 1 | 设计审查较快 |
Coding | 18 | 23 | 对编码工作量的估计有偏差 |
Code Review | 2 | 3 | 代码审查严格 |
Test | 8 | 7 | |
Reporting | 2 | 2 | |
Size Measurement | 1 | 1 | |
Postmortem | 2 | 1 | |
合计 | 53 | 68 | 时长似乎超出了我们的预期,究其原因是对 java微信小程序开发的流程及代码实现不够熟悉。 |
解题思路描述与设计实现说明
本次项目旨在解决校园内跨专业合作难题,通过微信小程序平台,实现项目发布、浏览、申请加入、沟通协作等功能。设计上采用模块化开发,前后端分离,提高开发效率和系统稳定性。
虽然有的细节方面仍有改进空间,但我们已在有限的时间内尽力完成开发。
设计实现
- 用户注册与认证:使用微信内置的登录功能,结合学号和身份证后六位进行认证。
- 项目发起:用户可以填写项目需求和目标,提交后在项目广场展示。
- 项目浏览与申请:用户浏览项目广场,对感兴趣的项目可以申请加入。
- 沟通协作:项目内部成员可以实时聊天,项目创建者可以管理项目状态和成员。
代码实现思路
文字描述
小程序前端使用微信开发者工具进行开发,后端使用Node.js进行接口编写。前端通过调用后端API进行数据交互。
关键流程图 - 个人中心
(为确保图像比例尺、提升观感,各个跳转界面的指令流程略)
重要代码片段
云函数 - 添加项目
我们实现了一个微信小程序的云函数脚本,用于实现添加项目的功能。
功能和意义:
-
项目创建:允许用户通过提供项目信息(如标题、发起人、成员、标签等)来创建新项目。
-
数据存储:将项目信息存储在云开发的数据库中。
-
错误处理:对可能出现的错误进行捕获和处理,确保用户得到明确的反馈。
实现方式:
- 云函数初始化:使用
wx-server-sdk
初始化云函数,指定当前云环境。 - 数据库操作:通过
cloud.database()
获取数据库操作对象。 - 添加项目:在
main
函数中接收项目信息,调用数据库的add
方法添加项目。 - 错误处理:使用
try...catch
结构捕获并处理可能发生的异常。
代码细节:
const cloud = require('wx-server-sdk');
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV // 使用当前云环境
});
const db = cloud.database();
// 云函数入口
exports.main = async (event, context) => {
const {title, initiator, members, tags, introduction, requirements, date } = event;
// 控制台输出输入的数据
console.log('接收到的参数:', { title, initiator, members, tags, introduction, requirements, date });
try {
const res = await db.collection('projects').add({
data: {
title: title,
initiator: initiator,
members: members,
tags: tags,
introduction: introduction,
requirements: requirements,
date: date || new Date() // 如果没有传入日期,则使用当前时间
}
});
// 控制台输出成功信息
console.log('项目创建成功:', res);
return {
success: true,
data: res
};
} catch (err) {
// 控制台输出错误信息
console.error('创建项目失败:', err);
return {
success: false,
errorMessage: err.message
};
}
};
个人中心
这段代码是一个微信小程序的页面逻辑脚本,用于实现个人中心页面的功能。
功能和意义:
- 用户信息展示与更新:页面用于展示和更新用户的基本信息,如头像、用户名、论文公开状态和通知启用状态。
- 头像更换:允许用户通过手机相册或相机选择新的头像,并上传到服务器。
- 用户名修改:允许用户修改自己的用户名,并将新用户名保存在本地存储和服务器。
- 密码修改:提供页面跳转,允许用户修改登录密码。
- 设置公开论文:用户可以选择是否公开自己的论文。
- 通知设置:用户可以启用或禁用应用通知。
- 项目管理:跳转到项目管理页面,允许用户进行项目相关的操作。
- 通知查看:跳转到通知页面,查看应用发送的通知。
实现方式:
- data:定义页面的初始状态,包括论文是否公开、通知是否启用、用户头像和用户名的默认值。
- onLoad:生命周期函数,在页面加载时执行。从本地存储中获取用户头像和用户名,并更新页面数据。
- changeAvatar:用户选择新头像后,更新页面显示的头像,并尝试上传到服务器。上传成功后,更新本地存储和页面数据。
- changeUsername:弹出模态对话框让用户输入新的用户名。用户确认后,更新页面数据和本地存储,并发送请求到服务器更新用户名。
- changePassword:跳转到修改密码的页面。
- togglePaperPublic:切换论文是否公开的设置,并发送请求到服务器更新该设置。
- toggleNotification:切换通知是否启用的设置,并发送请求到服务器更新该设置。
- goToProjectManagement:跳转到项目广场页面。
- goToNotifications:跳转到用户通知页面。
代码细节:
- wx.chooseImage:允许用户从相册或相机选择图片。
- wx.uploadFile:将选择的图片上传到服务器。
- wx.setStorageSync:将数据同步存储到本地。
- wx.request:发送网络请求到服务器,用于更新用户名、论文公开状态和通知设置。
- wx.showToast:显示操作结果的提示信息。
- wx.navigateTo:跳转到应用内的某个特定页面。
- 代码中有几个
wx.request
和wx.uploadFile
的请求地址,供演示用 wx.showToast
在操作失败时提供了用户反馈,增强了用户体验。wx.showModal
提供了一个交互式的对话框,让用户可以输入新的用户名。
详细代码如下:
Page({
data: {
isPaperPublic: false,
isNotificationEnabled: true,
avatarUrl: 'default-avatar-url', // 默认头像链接
username: 'default-username' // 默认用户名
},
onLoad: function() {
// 获取用户信息并设置头像和用户名
this.setData({
avatarUrl: wx.getStorageSync('avatarUrl') || 'default-avatar-url',
username: wx.getStorageSync('username') || 'default-username'
});
},
changeAvatar() {
wx.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
const tempFilePaths = res.tempFilePaths;
this.setData({
avatarUrl: tempFilePaths[0]
});
// 上传头像到服务器
wx.uploadFile({
url: 'https://your-server-url.com/upload-avatar',
filePath: tempFilePaths[0],
name: 'file',
success: (res) => {
const data = JSON.parse(res.data);
const newAvatarUrl = data.url;
this.setData({ avatarUrl: newAvatarUrl });
wx.setStorageSync('avatarUrl', newAvatarUrl);
},
fail: (err) => {
wx.showToast({
title: '头像上传失败,请稍后再试',
icon: 'none'
});
}
});
}
});
},
changeUsername() {
wx.showModal({
title: '修改用户名',
content: '',
placeholderText: '请输入新的用户名',
editable: true,
success: (res) => {
if (res.confirm) {
const newUsername = res.content;
this.setData({ username: newUsername });
wx.setStorageSync('username', newUsername);
// 更新用户名到服务器
wx.request({
url: 'https://your-server-url.com/update-username',
method: 'POST',
data: { username: newUsername },
success: (res) => {
wx.showToast({
title: '用户名修改成功',
icon: 'success'
});
},
fail: (err) => {
wx.showToast({
title: '用户名修改失败,请稍后再试',
icon: 'none'
});
}
});
}
}
});
},
changePassword() {
wx.navigateTo({
url: '/pages/changePassword/changePassword'
});
},
togglePaperPublic(e) {
this.setData({
isPaperPublic: e.detail.value
});
// 更新设置到服务器
wx.request({
url: 'https://your-server-url.com/update-settings',
method: 'POST',
data: { isPaperPublic: e.detail.value },
success: (res) => {
wx.showToast({
title: '设置已更新',
icon: 'success'
});
},
fail: (err) => {
wx.showToast({
title: '设置更新失败,请稍后再试',
icon: 'none'
});
}
});
},
toggleNotification(e) {
this.setData({
isNotificationEnabled: e.detail.value
});
// 更新设置到服务器
wx.request({
url: 'https://your-server-url.com/update-settings',
method: 'POST',
data: { isNotificationEnabled: e.detail.value },
success: (res) => {
wx.showToast({
title: '设置已更新',
icon: 'success'
});
},
fail: (err) => {
wx.showToast({
title: '设置更新失败,可能是尚未连接到服务器,请稍后再试',
icon: 'none'
});
}
});
},
goToProjectManagement() {
wx.navigateTo({
url: '/pages/xiangmuguangchang/xiangmuguangchang'
});
},
goToNotifications() {
wx.navigateTo({
url: '/pages/wodetongzhi/wodetongzhi'
});
}
});
附加特点设计与展示
设计的创意独到之处
- 数据存储与同步
- 本地存储:使用
wx.setStorageSync()
和wx.getStorageSync()
在本地存储和获取用户信息,如头像和用户名。 - 服务器同步:通过
wx.request()
将用户信息(如用户名、设置选项)同步到服务器。
- 本地存储:使用
- 错误处理与用户反馈
- 在多个操作(如上传头像、修改用户名、切换设置等)中,包含错误处理和用户反馈机制,如使用
wx.showToast()
显示操作结果。
- 在多个操作(如上传头像、修改用户名、切换设置等)中,包含错误处理和用户反馈机制,如使用
- 用户体验设计
- 模态对话框:使用
wx.showModal()
让用户输入新的用户名,提供更好的用户体验。 - 页面反馈:在用户完成操作(如上传头像、修改设置等)后,提供即时反馈。
- 模态对话框:使用
- 安全性考虑
- 密码修改:提供页面跳转进行密码修改,增强账户安全性。
- 登录逻辑:包含基本的登录逻辑,可能与后端服务结合以验证用户身份。
- 动态数据绑定
- 使用
this.setData()
动态更新页面数据,如头像、用户名等,确保用户界面与内部数据状态保持同步。
- 使用
实现思路
实现思路分析
1. 数据存储与同步
本地存储:
- 思路:利用微信小程序提供的本地存储API
wx.setStorageSync()
和wx.getStorageSync()
实现用户信息的本地保存和读取。 - 实现:
- 在用户更改头像或用户名后,使用
wx.setStorageSync('key', 'value')
将新的信息存储在本地。 - 在页面加载时,使用
wx.getStorageSync('key')
获取并设置用户信息,确保用户信息的持久化和快速读取。
- 在用户更改头像或用户名后,使用
服务器同步:
- 思路:通过HTTP请求将用户信息更新到服务器,确保多设备间的数据一致性。
- 实现:
- 使用
wx.request()
发送POST请求到服务器的特定API,如'https://your-server-url.com/update-username'
。 - 在请求的data中包含需要更新的信息,如
{ username: newUsername }
。 - 根据服务器响应判断操作是否成功,并给予用户相应的反馈。
- 使用
2. 错误处理与用户反馈
- 思路:在每个可能发生错误的关键操作后添加错误处理逻辑,并及时反馈给用户。
- 实现:
- 在网络请求的
fail
回调中使用wx.showToast()
显示错误信息,如wx.showToast({ title: '操作失败,请稍后再试', icon: 'none' })
。 - 在操作成功时显示成功提示,增强用户的交互体验。
- 在网络请求的
3. 用户体验设计
模态对话框:
- 思路:使用模态对话框让用户在当前页面完成操作,避免跳转,减少用户操作成本。
- 实现:
- 通过
wx.showModal()
弹出模态对话框,让用户输入新的用户名。 - 设置
placeholderText
提示用户输入内容,使用editable: true
允许用户编辑。
- 通过
页面反馈:
- 思路:在用户进行操作后给予即时反馈,让用户知道操作已被执行。
- 实现:
- 在用户上传头像或修改设置后使用
wx.showToast()
显示操作结果,如wx.showToast({ title: '修改成功', icon: 'success' })
。
- 在用户上传头像或修改设置后使用
4. 安全性考虑
密码修改:
- 思路:提供专门的页面让用户修改密码,确保密码修改的安全性。
- 实现:
- 使用
wx.navigateTo()
跳转到密码修改页面,如wx.navigateTo({ url: '/pages/changePassword/changePassword' })
。
- 使用
登录逻辑:
- 思路:结合微信登录和传统的账号密码登录,确保登录的安全性。
- 实现:
- 使用
wx.login()
获取登录凭证(code)。 - 将code发送到服务器进行身份验证和会话管理。
- 使用
5. 动态数据绑定
-
思路:利用小程序的数据绑定机制,实时更新用户界面。
-
实现:
-
使用
this.setData()
更新页面的数据,如this.setData({ avatarUrl: newAvatarUrl })
。 -
确保页面显示的数据与内部状态同步,提升应用的响应性和交互性。
-
重要代码片段
添加项目的云函数:
const cloud = require('wx-server-sdk');
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV // 使用当前云环境
});
const db = cloud.database();
// 云函数入口
exports.main = async (event, context) => {
const {title, initiator, members, tags, introduction, requirements, date } = event;
// 控制台输出输入的数据
console.log('接收到的参数:', { title, initiator, members, tags, introduction, requirements, date });
try {
const res = await db.collection('projects').add({
data: {
title: title,
initiator: initiator,
members: members,
tags: tags,
introduction: introduction,
requirements: requirements,
date: date || new Date() // 如果没有传入日期,则使用当前时间
}
});
// 控制台输出成功信息
console.log('项目创建成功:', res);
return {
success: true,
data: res
};
} catch (err) {
// 控制台输出错误信息
console.error('创建项目失败:', err);
return {
success: false,
errorMessage: err.message
};
}
};
本地存储:
// 保存用户头像到本地存储
wx.setStorageSync('avatarUrl', newAvatarUrl);
// 获取保存的用户头像
this.setData({
avatarUrl: wx.getStorageSync('avatarUrl') || 'default-avatar-url',
});
wx.setStorageSync('avatarUrl', newAvatarUrl)
:将新的用户头像URL保存到本地存储中,键为avatarUrl
。wx.getStorageSync('avatarUrl')
:从本地存储中检索头像URL。如果不存在,使用默认头像。
服务器同步:
// 同步用户名到服务器
wx.request({
url: 'https://your-server-url.com/update-username',
method: 'POST',
data: { username: newUsername },
success: (res) => {
wx.showToast({
title: '用户名修改成功',
icon: 'success'
});
},
fail: (err) => {
wx.showToast({
title: '用户名修改失败,请稍后再试',
icon: 'none'
});
}
});
- 使用
wx.request
发送一个POST请求到服务器的/update-username
端点。 - 在请求的data中传递新的用户名
newUsername
。 - 如果请求成功,显示一个成功的提示。
- 如果请求失败,显示一个错误提示。
错误处理与用户反馈:
// 用户头像上传失败反馈
fail: (err) => {
wx.showToast({
title: '头像上传失败,请稍后再试',
icon: 'none'
});
}
- 在上传头像的
wx.uploadFile
请求失败时,使用wx.showToast
显示一个错误提示。
模态对话框:
// 弹出模态对话框修改用户名
wx.showModal({
title: '修改用户名',
content: '',
placeholderText: '请输入新的用户名',
editable: true,
success: (res) => {
if (res.confirm) {
const newUsername = res.content;
this.setData({ username: newUsername });
wx.setStorageSync('username', newUsername);
wx.request({
url: 'https://your-server-url.com/update-username',
method: 'POST',
data: { username: newUsername },
success: (res) => {
wx.showToast({
title: '用户名修改成功',
icon: 'success'
});
},
fail: (err) => {
wx.showToast({
title: '用户名修改失败,请稍后再试',
icon: 'none'
});
}
});
}
}
});
-
wx.showModal
:弹出一个模态对话框,提供输入框让用户可以编辑新的用户名。 -
res.confirm
:检查用户是否点击了确定按钮。 -
res.content
:获取用户输入的新用户名。
实现成果展示
(个人中心、私聊、创建项目、查看项目、搜索项目)
目录说明和使用说明
目录组织
ProjectPartner/
├── pages/ # 存放所有页面
│ ├── xiangmuguanli/ # 项目管理页面
│ ├── dengru/ # 登录页面
│ ├── wangjimima/ # 忘记密码页面
│ ├── xiugaimima/ # 修改密码页面
│ ├── chuangzhimima/ # 重置密码页面
│ ├── xiangmuguangchang/ # 项目广场页面
│ ├── index/ # 首页
│ ├── xiangmuxiangqing/ # 项目详情页面
│ ├── cloud #创建项目页面
│ ├── zhuanjiajiaoshou/ # 专家教授页面
│ ├── gerenzhongxin/ # 个人中心
│ ├── wodetongzhi/ # 我的通知
│ ├── chat/ # 聊天页面
│ ├── shenqing/ # 申请页面
│ └── chatli/ # 聊天列表页面
├── utils/ # 存放工具类
├── components/ # 存放自定义组件
├── app.js # 小程序逻辑
├── app.json # 小程序全局配置
└── sitemap.json # 用于小程序的 SEO
使用说明
如何运行小程序?
使用微信,扫描我们通过“预览”按钮生成小程序二维码,但遗憾的是该二维码因审核未通过无法正常使用:
- 安装依赖:
- 确保你已安装微信开发者工具。
- 打开源代码、导入项目:
- 打开我们的Github仓库,点击【FZU跨专业合作小程序项目】以浏览我们的项目(其它文件是旧版本)
- 打开微信开发者工具,点击“文件”->“打开项目”,选择我们的小程序项目文件夹,导入项目。
- 编译运行:
- 点击“编译”按钮或按
Ctrl+Enter
(在Mac上是Cmd+Enter
)编译运行项目。
- 点击“编译”按钮或按
单元测试
测试工具
我们使用微信开发者工具内置的测试功能进行单元测试。
简易教程
确保已安装jest
和mockjs
库后,在test
目录下编写测试代码,如apptest.js
,然后在微信开发者工具中,点击“测试”按钮,选择要运行的测试用例,查看测试结果,进行单元测试。
以我们对【个人中心】(pages/gerenzhongxin/gerenzhongxin.js)
的测试为例,您可以参考以下方法进行测试:
首先,确保你在项目根目录下的app.json
中开启测试功能:
{
"miniprogram": {
"pages": [
// ... 你的页面配置
],
"window": {
// ... 你的窗口配置
},
// ... 其它配置
"test": {
"component": true,
"page": true,
"coverage": true
}
}
}
然后,创建测试文件,例如针对pages/gerenzhongxin/gerenzhongxin.js
的测试文件为test/gerenzhongxin.test.js
,实际代码详见【部分单元测试代码】
最后在微信开发者工具中,点击“测试”按钮,选择测试文件gerenzhongxin.test.js
,然后点击“运行测试”。
Tips:
-
miniprogram-mock
库用于模拟微信小程序的API,以便在单元测试中使用。 -
assert
库用于断言测试结果是否符合预期。 -
需要模拟的API,比如
wx.getStorageSync
、wx.showModal
、wx.setStorageSync
和wx.request
,根据实际测试需要进行模拟。 -
测试用例使用
describe
和it
块组织,每个it
块是一个测试点。
部分单元测试代码
我们对个人中心修改操作(包括模拟网络请求和本地存储)的测试代码如下:
const miniprogram = require('miniprogram-mock');
const assert = require('assert');
const mock = require('mockjs');
// 模拟wx API
miniprogram.mock({
wx: {
getStorageSync: function (key) {
return miniprogram.getStorageSync(key);
},
setStorageSync: function (key, value) {
return miniprogram.setStorageSync(key, value);
},
showToast: function (options) {
if (options.icon === 'none') {
console.log(options.title);
}
},
chooseImage: function (options) {
if (options.success) {
options.success({
tempFilePaths: [mock.Random.dataImage('200x200', '#50B347', '#FFF', 'Mock')]
});
}
},
uploadFile: function (options) {
if (options.success) {
options.success({
data: JSON.stringify({ url: mock.Random.image('200x200', '#50B347', '#FFF', 'Mock'))
});
}
},
showModal: function (options) {
if (options.success) {
options.success({ content: 'NewUsername' });
}
},
request: function (options) {
if (options.success) {
options.success({
data: {
url: mock.Random.image('200x200', '#50B347', '#FFF', 'Mock')
}
});
} else if (options.fail) {
options.fail({ error: 'request fail' });
}
},
navigateTo: function (options) {
console.log('navigateTo', options.url);
}
}
});
// 引入待测试的页面逻辑
const gerenzhongxin = require('../../pages/gerenzhongxin/gerenzhongxin.js').Page;
// 测试页面加载
describe('gerenzhongxin Page', () => {
it('should load data from storage on load', () => {
miniprogram.setStorageSync('avatarUrl', 'stored-avatar-url');
miniprogram.setStorageSync('username', 'stored-username');
const page = new gerenzhongxin();
page.onLoad();
assert.equal(page.data.avatarUrl, 'stored-avatar-url');
assert.equal(page.data.username, 'stored-username');
});
// 测试头像更换
it('should change avatar and update storage', () => {
const page = new gerenzhongxin();
page.changeAvatar();
assert.equal(page.data.avatarUrl, 'mocked-temp-file-path');
const uploadedAvatarUrl = mock.Random.image('200x200', '#50B347', '#FFF', 'Mock');
miniprogram.getStorageSync = () => uploadedAvatarUrl;
page.setData({ avatarUrl: uploadedAvatarUrl }, () => {
assert.equal(page.data.avatarUrl, uploadedAvatarUrl);
});
});
// 测试用户名修改
it('should change username and update storage', () => {
const page = new gerenzhongxin();
page.changeUsername();
assert.equal(page.data.username, 'NewUsername');
const newUsername = 'NewUsername';
miniprogram.getStorageSync = () => newUsername;
page.setData({ username: newUsername }, () => {
assert.equal(page.data.username, newUsername);
});
});
// 测试论文公开状态切换
it('should toggle paper public status', () => {
const page = new gerenzhongxin();
page.togglePaperPublic({ detail: { value: true } });
assert.equal(page.data.isPaperPublic, true);
page.togglePaperPublic({ detail: { value: false } });
assert.equal(page.data.isPaperPublic, false);
});
// 测试通知启用状态切换
it('should toggle notification enabled status', () => {
const page = new gerenzhongxin();
page.toggleNotification({ detail: { value: false } });
assert.equal(page.data.isNotificationEnabled, false);
page.toggleNotification({ detail: { value: true } });
assert.equal(page.data.isNotificationEnabled, true);
});
// 测试密码修改页面跳转
it('should navigate to change password page', () => {
const page = new gerenzhongxin();
const navigateTo = miniprogram.wx.navigateTo = jest.fn();
page.changePassword();
expect(navigateTo).toHaveBeenCalledWith({
url: '/pages/changePassword/changePassword'
});
})
//跳转到其它页面的其余测试代码略
});
部分单元测试代码解释
- 模拟wx API:
- 使用
miniprogram.mock
模拟微信小程序的API,如wx.getStorageSync
、wx.setStorageSync
、wx.showToast
等。 wx.chooseImage
和wx.uploadFile
模拟用户选择图片和上传图片的操作。wx.showModal
和wx.request
模拟用户输入和网络请求操作。
- 使用
- 引入待测试的页面逻辑:
- 使用
require
引入待测试的页面逻辑gerenzhongxin.js
。
- 使用
- 测试页面加载:
- 使用
miniprogram.setStorageSync
模拟本地存储的数据。 - 创建页面实例并调用
onLoad
方法,检查页面数据是否正确加载。
- 使用
- 测试头像更换:
- 调用
changeAvatar
方法,模拟用户选择新头像并上传。 - 检查页面数据中的头像URL是否更新。
- 调用
- 测试用户名修改:
- 调用
changeUsername
方法,模拟用户输入新的用户名。 - 检查页面数据中的用户名是否更新。
- 调用
- 测试论文公开状态切换:
- 调用
togglePaperPublic
方法,模拟用户切换论文公开状态。 - 检查页面数据中的
isPaperPublic
是否正确更新。
- 调用
- 测试通知启用状态切换:
- 调用
toggleNotification
方法,模拟用户切换通知启用状态。 - 检查页面数据中的
isNotificationEnabled
是否正确更新。
- 调用
- 测试密码修改页面跳转:
- 调用
changePassword
方法,模拟跳转到密码修改页面。 - 使用
jest.fn()
模拟wx.navigateTo
,检查是否跳转到正确的页面。
- 调用
- 测试跳转到项目管理页面:
- 调用
goToProjectManagement
方法,模拟跳转到项目管理页面。 - 使用
jest.fn()
模拟wx.navigateTo
,检查是否跳转到正确的页面。
- 调用
- 测试跳转到通知页面:
- 调用
goToNotifications
方法,模拟跳转到通知页面。 - 使用
jest.fn()
模拟wx.navigateTo
,检查是否跳转到正确的页面。
- 调用
测试数据思路
我们构造了各种用户操作场景,包括正常操作和异常操作,确保程序能够正确处理。
构造测试数据的思路:
- 正常情况:构造符合函数或方法预期输入的数据。例如,如果测试用户名更改功能,构造一个包含有效新用户名的测试用例。
- 边界情况:考虑输入数据在边界上的情况,如空字符串、最大长度限制等。
- 异常情况:设计一些使程序可能出现错误的输入,比如非法字符、特殊字符、超长字符串等。
- 依赖数据:对于依赖外部数据或状态的函数,模拟这些依赖项,确保测试环境的一致性和可重复性。
- 返回值:模拟函数依赖的外部调用的返回值,例如API请求的响应。
- 错误模拟:模拟函数内部可能抛出的错误,验证是否有恰当的错误处理机制。
- 性能测试:构造大量数据测试函数的性能表现,确保在数据量较大时仍能正常工作。
- 安全性测试:构造可能引起安全问题的输入,如SQL注入、跨站脚本(XSS)等,验证安全性处理。
考虑未来***难的策略:
- 全面覆盖:确保测试覆盖所有的代码路径,包括所有的条件分支。
- 使用断言:在测试中使用断言验证程序行为是否符合预期。
- 模拟和存根:对于外部依赖,使用模拟(Mock)和存根(Stub)来控制和预测它们的返回值。
- 测试隔离:确保每个测试用例相互独立,一个测试的执行不依赖于另一个测试的结果。
- 异常测试:除了测试正常流程外,还要测试异常流程,如网络请求失败、用户输入错误等。
- 持续集成:将测试集成到持续集成(CI)流程中,确保每次代码提交都运行测试。
- 代码覆盖率:使用代码覆盖率工具来检查测试是否覆盖到了所有的代码。
- 探索性测试:进行一些非正式的、基于探索的手动测试,以发现可能未被自动化测试捕获的问题。
Github代码签入记录截图
我们在实际代码编写中经常修改不同的文件以至同步性略有问题,因此我们更多采用【线下沟通+QQ传输压缩包】的方式来进行协作编写。以下仅为极小部分的有签入的记录截图,以及我们QQ互传的部分记录
遇到的问题及解决方法
在实现“个人中心”页面的头像上传功能时,我们发现用户上传的新头像有时没有正确更新到页面上。
做过哪些尝试
- 检查数据绑定:我们首先检查了
this.setData()
调用是否正确,确保新头像的URL被正确设置到页面数据上。 - 调试网络请求:我们使用了微信开发者工具的网络调试功能,检查上传头像的请求是否成功,以及服务器响应是否正确。
- 检查回调函数:我们确保在上传成功后的回调函数中正确处理了服务器返回的新头像URL。
- 使用小程序的存储功能调试:我们尝试将上传后的头像URL存储到本地存储中,然后在页面加载时读取,看是否能够成功显示。
问题最终得到解决。通过解决这个问题,我们更深入地了解了小程序的数据绑定机制和网络请求的处理方式。我们还学会了如何更有效地使用开发者工具进行调试,这提高了我们的问题解决能力。
评价你的队友
陆旭东 To_ 陈康培 (To 102201120)
值得学习的地方:
- 在小程序开发方面有着扎实的技术功底,特别是在前端页面设计和逻辑编写方面表现突出。
- 遇到技术难题时能够冷静分析问题并找到有效的解决方案。
- 项目开发过程中能够主动沟通、积极协调,确保团队合作顺利进行。
- 对分配给自己的任务非常负责,能够高质量完成
需要改进的地方:
- 在项目初期对时间的管理有待提高,有时候会拖延,但随着项目的推进,他逐渐调整并改善了这一点。
- 有时会忽视一些细节处理,需要在以后的工作中更加注重细节。
陈康培 To_ 陆旭东 (To 102201118)
值得学习的地方:
- 沟通能力强,能够清晰地表达自己的想法,并有效地与队友进行交流。
- 在项目设计过程中总能提出一些创新的想法,为项目增加亮点。
- 对新技术有着浓厚的兴趣,学习态度积极,能够快速掌握并应用到项目中。
- 面对项目需求的变化能够迅速适应并调整自己的工作节奏和方法。
需要改进的地方:
- 在项目压力较大时,会显得有些焦虑,可以学着更好调节自己的情绪和压力。
- 某些时候会过于专注于一些技术细节导致耗时,需要在未来的项目中更加合理地分配时间。