New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mock Testing Framework #1521
Mock Testing Framework #1521
Conversation
mirai-core/src/jvmMock/kotlin/contact/announcement/MockAnnouncements.kt
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resoved
完成了一遍完整 review, 总体来说挺好。这套 mock API 重新实现了 core Contact,去掉所有网络请求但保留广播事件的行为;为各 Contact 添加了新的属性和方法来允许模拟来自服务器的变化,并兼顾实现了事件的广播。
待解决的问题
在 Kotlin 写测试
测试肯定会涉及到事件处理,例如要期望程序对一个变更回复一条消息,测试程序则会期待 GroupMessagePostSendEvent
*(不太记得名字了)*的广播。如何在 Kotlin 方便地接收这个事件?
val list = ConcurrentLinkedQueue<GroupMessagePostSendEvent>()
val listener = GlobalEventChannel.subscribeAlways<GroupMessagePostSendEvent> { list.add(it) }
// test code
listener.cancel()
这样的工具函数应该是必要的。
可能的优化
区分模拟事件和响应事件
测试框架里模拟的属性变更与待测程序调用的方法是相同的,如何分辨是来自程序还是来自测试?如何知晓程序是否对事件作出反应?现在可能只能通过接收事件来解决,但考虑以下场景:
见示例:
程序实现当一个人群名片有修改时, 就同时修改另一个人的群名片
// 待测程序
eventChannel.subscribeAlways<MemberNameCardChangedEvent> { event ->
group[123456].nameCard = event.new
}
// 测试代码
val collected = collectBroadcastEvents { // 伪, 收集该 block 内产生的所有事件
member.nameCard = "A" // 模拟来自服务器的变更
}
collected
有两个事件----模拟过程会产生一个事件,然后处理时又会产生一个。可以通过 drop 第一个事件来解决问题,但若过程更复杂一些:
// 测试代码
val collected = collectBroadcastEvents { // 伪, 收集该 block 内产生的所有事件
member.nameCard = "A" // 模拟来自服务器的变更
member2.nameCard = "B" // 模拟来自服务器的变更
member3.nameCard = "C" // 模拟来自服务器的变更
// 假设这个功能需要这样的连续事件
}
可能需要考虑优化这种情况,即在接收后或广播前过滤模拟事件。
'响应式'消息处理
一个'响应式'消息处理场景:用户输入三条消息:"查询成绩","张三","1000100"(学号)
assertEventCollected<MessagePostSendEvent> { member says "查询成绩" }.run {
assertEquals("请输入您的姓名", message.content)
}
assertEventCollected<MessagePostSendEvent> { member says "张三" }.run {
assertEquals("请输入您的学号", message.content)
}
assertEventCollected<MessagePostSendEvent> { member says "1000100" }.run {
assertEquals("您的成绩是 100", message.content)
}
这样的处理可能过于冗长了,若有相关 DSL 将能大幅提升体验:
val marks = 100
database.setStudentMarks(1000100, marks) // 在数据库设置成绩
assertInteractiveMessaging {
member says "查询成绩"
bot replies "请输入您的姓名"
member says "张三"
bot replies "请输入您的学号"
member says "1000100"
bot replies { "您的成绩是 $marks" }
}
says 和 replies 的实现应该是即时的,这样用户可以在对话中任意穿插其他的 assert。
制造模拟事件
在响应式消息测试 DSL 设想之上考虑,前文提到的关于模拟事件筛选的问题,以广播前过滤的方式实现应该更好,还可以借助 DSL,通过如下语法实现模拟事件
withMockContext(group) { // this: MockGroupDslContext
member nickChangesTo "test" // 一般现在时, 声明并广播一条来自服务器的事件:用户昵称变为 test。
bot replied "OK" // 一般过去时, assert, 从刚刚 collect 的事件中 pop 一个并判断
member.nudges(bot) { // 服务器事件:用户戳了 bot
action = "拍了拍"
suffix = "的肩膀"
}
bot nudged member // assert 一个反馈
我只以一个用户的角度简单考虑了 DSL 语法,实现起来可能要更全面考虑(例如 Java)。
实现广播前筛选
要避免 collect 到模拟事件,一个方法是为事件 coroutineContext 添加一个 marker element,然后在 collect 时检测。这可能会要修改一部分 core 实现,当然这也需要实现类似上文 withMockContext
的事件模拟方式才能做到修改 coroutineContext 。
该框架是是设计用来测试 |
053f354
to
3e8fdd3
Compare
dfb41f7
to
da2d0f0
Compare
adaa830
to
1261ebf
Compare
7fb2ce7
to
93783e2
Compare
320ec01
to
55ac70a
Compare
55ac70a
to
0bd1f84
Compare
03fac45
to
7636a31
Compare
今天必review |
fb108fc
to
d73b8db
Compare
主要是 api 部分 |
d73b8db
to
9c804bd
Compare
在我 review 完之前能不能先不要 force,force 一次就白 review 了 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
不要 rebase, 不要 rebase, 不要 rebase,
等我 APPROVE 了再 rebase, 等我 APPROVE 了再 rebase, 等我 APPROVE 了再 rebase,
还有之前的问题你没处理, 现在 rebase 了记录也没了我重写一遍
一些函数真的有必要放在 mirai-core-utils 么, 它们更应该在 mock framework 的模块中
去除所有包名和类名中的 tx
0c55716
to
a2cff6c
Compare
曾经有个人让你别 force push,现在那个人又要重新 review 35个文件 |
f46d6f1
to
119c373
Compare
257ae2f
to
03e0b91
Compare
@Karlatemp Your opportunity to merge |
Co-authored-by: Eritque arcus <1930893235@qq.com>
03e0b91
to
9cfb78d
Compare
@Him188 另外再看一下文档有没有问题,没有问题一会 ci 过了就和了 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
看了文档
close #1304
TODO:
Contacts
Messages
Member -> Group
,XXX -> Bot
)Events
Bot
Friend
Stranger
Group
Misc
IMirai.queryProfile
)RemoteFile
(mirai remote file api v1)RemoteFiles
(mirai remote file api v2)