Python 的单元测试无法为 Discord 机器人测试传递参数“缺少 1 个必需的位置参数:'ctx'”?
Posted
技术标签:
【中文标题】Python 的单元测试无法为 Discord 机器人测试传递参数“缺少 1 个必需的位置参数:\'ctx\'”?【英文标题】:Python's unittest fails to pass in parameter "missing 1 required positional argument: 'ctx'" for Discord bot testing?Python 的单元测试无法为 Discord 机器人测试传递参数“缺少 1 个必需的位置参数:'ctx'”? 【发布时间】:2021-11-29 07:15:20 【问题描述】:我正在尝试为我的 Discord.py 命令之一编写一个简单的测试用例:
https://github.com/NobleUplift/Hierarchies/blob/master/cogs/HierarchyManagement.py#L32
函数定义很简单:
async def list(self, ctx: discord.ext.commands.Context):
我正在尝试编写一个单元测试,暂时将 ctx.send
替换为 print
,以验证我的 Discord 命令的输出,最终目标是添加验证命令输出的断言:
Hierarchies:
• us-army
• admins
这是我的完整测试用例:
import unittest
from unittest.mock import Mock
from unittest.mock import patch
from cogs.HierarchyManagement import HierarchyManagement
class test_Hierarchies(unittest.IsolatedAsyncioTestCase):
async def test_list(self):
bot = Mock() # commands.Bot()
ctx = Mock() # discord.ext.commands.Context()
ctx.message.content = '^list'
ctx.message.guild.id = 870166294127837205
ctx.author.mention = '<@368187588340875284>'
ctx.author.name = 'NobleUplift'
ctx.author.discriminator = '1038'
ctx.send = lambda retval: print(retval)
print(ctx.message.guild.id)
hierarchyManagement = HierarchyManagement(bot)
print(ctx)
print(hierarchyManagement)
await hierarchyManagement.list(ctx)
if __name__ == '__main__':
unittest.main()
这是我收到的错误:
Error
Traceback (most recent call last):
File "C:\Program Files\Python38\lib\unittest\case.py", line 60, in testPartExecutor
yield
File "C:\Program Files\Python38\lib\unittest\case.py", line 676, in run
self._callTestMethod(testMethod)
File "C:\Program Files\Python38\lib\unittest\async_case.py", line 65, in _callTestMethod
self._callMaybeAsync(method)
File "C:\Program Files\Python38\lib\unittest\async_case.py", line 88, in _callMaybeAsync
return self._asyncioTestLoop.run_until_complete(fut)
File "C:\Program Files\Python38\lib\asyncio\base_events.py", line 616, in run_until_complete
return future.result()
File "C:\Program Files\Python38\lib\unittest\async_case.py", line 102, in _asyncioLoopRunner
ret = await awaitable
File "Hierarchies\cogs\test_HierarchyManagement.py", line 21, in test_list
await hierarchyManagement.list(ctx)
File "Python38\site-packages\discord\ext\commands\core.py", line 374, in __call__
return await self.callback(*args, **kwargs)
TypeError: list() missing 1 required positional argument: 'ctx'
为什么它告诉我它需要ctx
当我很清楚地传入 ctx 变量时?
【问题讨论】:
【参考方案1】:这里的问题是当你定义一个 discord.py 命令时,你需要使用 @command()
装饰器。装饰器自动进行一些转换并将HierarchyManagement.list
重新定义为代表list
命令的discord.Command
对象。由于在没有实例 self
的情况下运行装饰器,因此您的 list
方法将作为无自我函数存储在 hierarchyManagement.list._callback
。
当您调用hierarchyManagement.list()
时,您实际上是在调用重定向到hierarchyManagement.list._callback()
的commands.Command.__call__()
dunder 方法。而且我们之前确定回调没有self
引用。
这就是为什么,你需要这样做:
await hierarchyManagement.list(hierarchyManagement, ctx)
discord.py 运行时它“神奇”地工作的原因是因为在命令参数解析期间,它们在运行时将self
注入到函数回调中。由于直接调用hierarchyManagement.list
会跳过所有解析,因此self
没有在您的情况下注入。
顺便说一句,ctx.send
是一个协程,但是当您在模拟 ctx.send = lambda retval: print(retval)
中定义它时,它是一个常规函数,稍后会引发错误。
【讨论】:
非常感谢!这让我发疯了。你知道什么好笑吗?我尝试同时执行hierarchyManagement.list(ctx=ctx)
和 hierarchyManagement.list(hierarchyManagement=hierarchyManagement, ctx=ctx)
但我没有听到错误告诉我使用位置参数而不是关键字参数并将 2+2 放在一起。
你知道如何将ctx.send
模拟为协程以便打印到控制台吗?
@NobleUplift 不使用 lambda,而是使用 async def send(*args, **kwargs): print(args, kwargs)
并设置 ctx.send = send
我在周末开始工作并开始编写我的测试用例。谢谢您的帮助!很高兴终于开始使用这个机器人进行测试驱动开发。以上是关于Python 的单元测试无法为 Discord 机器人测试传递参数“缺少 1 个必需的位置参数:'ctx'”?的主要内容,如果未能解决你的问题,请参考以下文章
python单元测试中无法使用谷歌浏览器打开测试报告的解决办法