Skip to content
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

Merged
merged 11 commits into from Sep 10, 2022
Merged

Mock Testing Framework #1521

merged 11 commits into from Sep 10, 2022

Conversation

Karlatemp
Copy link
Member

@Karlatemp Karlatemp commented Sep 5, 2021

close #1304


TODO:

Contacts

  • MockBot
  • Mock Friend
  • Mock Group
    • Mock Normal Member
    • Mock Anonymous Member
  • Mock Stranger
  • Mock Otherclient

Messages

  • Friend -> Bot
  • Stranger -> Bot
  • Normal Member -> Group
  • Anonymous Member -> Group
  • Bot -> Friend
  • Bot -> Group
  • Bot -> Stranger
  • Friend message recall
  • Normal member message recall
  • RoamingMessage
    • Friend
  • Audio sending
    • Resource as online audio (For Member -> Group, XXX -> Bot)
  • MessageSyncEvent
    • GroupMessageSyncEvent
    • FriendMessageSyncEvent

Events

Bot

  • BotOnlineEvent
  • BotOfflineEvent
  • BotReloginEvent
  • BotAvatarChangedEvent
  • BotNickChangedEvent

Friend

  • NewFriendRequestEvent
  • FriendAddEvent
  • FriendDeleteEvent
  • FriendRemarkChangeEvent
  • FriendAvatarChangedEvent
  • FriendNickChangedEvent
  • FriendInputStatusChangedEvent

Stranger

  • StrangerAddEvent
  • StrangerRelationChangeEvent

Group

  • GroupNameChangeEvent
  • GroupEntranceAnnouncementChangeEvent
  • GroupMuteAllEvent
  • GroupAllowAnonymousChatEvent
  • GroupAllowConfessTalkEvent
  • GroupAllowMemberInviteEvent
  • BotGroupPermissionChangeEvent
  • BotMuteEvent
  • BotUnmuteEvent
  • BotInvitedJoinGroupRequestEvent
  • BotJoinGroupEvent
  • MemberJoinRequestEvent
  • MemberJoinEvent
  • MemberLeaveEvent.Kick
    • Kick by bot
    • Kick by others
  • MemberLeaveEvent.Quit
  • MemberCardChangeEvent
  • MemberSpecialTitleChangeEvent
  • MemberPermissionChangeEvent
  • MemberMuteEvent
  • MemberUnmuteEvent
  • MemberHonorChangeEvent
  • GroupTalkativeChangeEvent

Misc

  • NudgeEvent
  • User profile query (IMirai.queryProfile)
  • Group Announcements
  • Group remote files
    • RemoteFile (mirai remote file api v1)
    • RemoteFiles (mirai remote file api v2)

@Karlatemp Karlatemp added the t:feature 类型: 新特性 label Sep 5, 2021
mirai-core/build.gradle.kts Outdated Show resolved Hide resolved
mirai-core/src/commonMain/kotlin/BotWithComponents.kt Outdated Show resolved Hide resolved
mirai-core-mock/build.gradle.kts Outdated Show resolved Hide resolved
mirai-core/src/jvmMock/kotlin/MockBotFactory.kt Outdated Show resolved Hide resolved
mirai-core/src/jvmMock/kotlin/contact/MockMember.kt Outdated Show resolved Hide resolved
mirai-core/src/jvmMock/kotlin/utils/type.kt Outdated Show resolved Hide resolved
mirai-core/src/jvmMock/kotlin/utils/type.kt Outdated Show resolved Hide resolved
mirai-core/src/jvmMock/kotlin/utils/type.kt Outdated Show resolved Hide resolved
mirai-core/src/jvmMock/kotlin/utils/io.kt Outdated Show resolved Hide resolved
mirai-core/src/jvmMock/kotlin/utils/type.kt Outdated Show resolved Hide resolved
mirai-core/src/jvmMock/kotlin/MockBot.kt Outdated Show resolved Hide resolved
mirai-core/src/jvmMock/kotlin/MockBot.kt Outdated Show resolved Hide resolved
mirai-core/src/jvmMock/kotlin/contact/MockContact.kt Outdated Show resolved Hide resolved
mirai-core/src/jvmMock/kotlin/contact/MockGroup.kt Outdated Show resolved Hide resolved
Copy link
Member

@Him188 Him188 left a 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 。

@Karlatemp
Copy link
Member Author

在 Kotlin 写测试, '响应式'消息处理

该框架是是设计用来测试 main 的各种方法的, 也就是在 main 中已经有相关的事件监听函数用户才会在 test 编写相关测试代码, 如果需要提供则应该在 mirai-core-api 提供

@Karlatemp Karlatemp force-pushed the mock-testframework branch 2 times, most recently from 053f354 to 3e8fdd3 Compare September 17, 2021 14:35
@Karlatemp Karlatemp force-pushed the mock-testframework branch 2 times, most recently from dfb41f7 to da2d0f0 Compare October 31, 2021 09:03
@Nambers Nambers mentioned this pull request Feb 9, 2022
9 tasks
@Karlatemp Karlatemp marked this pull request as ready for review May 14, 2022 10:53
@Him188 Him188 self-requested a review May 23, 2022 17:18
@Him188 Him188 added this to the 2.13 milestone Jun 4, 2022
@Him188 Him188 modified the milestones: 2.13, 2.13.0-RC Jun 18, 2022
@Him188
Copy link
Member

Him188 commented Jun 19, 2022

今天必review

mirai-core-mock/README.md Outdated Show resolved Hide resolved
mirai-core-mock/src/MockBot.kt Outdated Show resolved Hide resolved
mirai-core-mock/src/MockBotFactory.kt Outdated Show resolved Hide resolved
mirai-core-mock/src/MockBotFactory.kt Outdated Show resolved Hide resolved
mirai-core-mock/src/contact/MockContact.kt Outdated Show resolved Hide resolved
mirai-core-utils/src/commonMain/kotlin/http.kt Outdated Show resolved Hide resolved
mirai-core-utils/src/jvmMain/kotlin/Image.jvm.kt Outdated Show resolved Hide resolved
mirai-core-mock/build.gradle.kts Outdated Show resolved Hide resolved
mirai-core-mock/build.gradle.kts Outdated Show resolved Hide resolved
mirai-core-mock/build.gradle.kts Outdated Show resolved Hide resolved
@Karlatemp Karlatemp force-pushed the mock-testframework branch 3 times, most recently from fb108fc to d73b8db Compare June 21, 2022 10:27
@Him188
Copy link
Member

Him188 commented Jun 24, 2022

rebase 之后
image

我死了

有哪需要 review 么,就不都看了

@Karlatemp
Copy link
Member Author

主要是 api 部分

@Him188
Copy link
Member

Him188 commented Jun 24, 2022

在我 review 完之前能不能先不要 force,force 一次就白 review 了

Copy link
Member

@Him188 Him188 left a 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

mirai-core-mock/src/MockBot.kt Outdated Show resolved Hide resolved
mirai-core-mock/src/MockBot.kt Outdated Show resolved Hide resolved
mirai-core-mock/src/contact/MockGroup.kt Outdated Show resolved Hide resolved
mirai-core-mock/src/contact/MockGroup.kt Outdated Show resolved Hide resolved
mirai-core-mock/src/contact/MockGroup.kt Outdated Show resolved Hide resolved
mirai-core-mock/src/internal/contact/MockFriendImpl.kt Outdated Show resolved Hide resolved
mirai-core-mock/src/internal/contact/MockGroupImpl.kt Outdated Show resolved Hide resolved
mirai-core-mock/src/utils/mockdsl.kt Outdated Show resolved Hide resolved
@Him188
Copy link
Member

Him188 commented Jul 22, 2022

曾经有个人让你别 force push,现在那个人又要重新 review 35个文件

@Him188 Him188 added the z:ready-to-merge 状态: PR 已经可以合并, 正在等待一些安排 label Sep 4, 2022
@Him188
Copy link
Member

Him188 commented Sep 4, 2022

@Karlatemp Your opportunity to merge

@Karlatemp
Copy link
Member Author

@Him188 另外再看一下文档有没有问题,没有问题一会 ci 过了就和了

Copy link
Member

@Him188 Him188 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

看了文档

@Karlatemp Karlatemp merged commit 2db9804 into dev Sep 10, 2022
@Karlatemp Karlatemp deleted the mock-testframework branch September 10, 2022 04:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
t:feature 类型: 新特性 z:ready-to-merge 状态: PR 已经可以合并, 正在等待一些安排
Projects
None yet
Development

Successfully merging this pull request may close these issues.

mirai-core 单元测试支持
2 participants