CraftSpider / dpytest

A package that assists in writing tests for discord.py
MIT License
103 stars 24 forks source link

channel.send and custom guilds #105

Open Debianov opened 1 year ago

Debianov commented 1 year ago

When the bot sends a message in response to dpytest.message, the message's author is the test user that was created in runner during configuration. As a result, one of the checks that occurs when the message is sent raises an AttributeError because it takes a user channel that tries to find the id of an auto-created user that does not exist in this guild channel:

@pytest.mark.asyncio
async def test_good_log_create():
    test_user = dpytest.backend.make_user("Test1", 2, id_num=386420570449051649)
    test_guild = dpytest.backend.make_guild(name="Test")
    test_channel = dpytest.backend.make_text_channel(guild=test_guild, name="Test")
    test_member = dpytest.backend.make_member(test_user, test_guild)
    await dpytest.message("something that starts the execution of one of the discord.py functions containing send() in the body", channel=test_channel, member=test_user)
File "lib/python3.11/site-packages/discord/abc.py", line 718, in permissions_for
    if self.guild.owner_id == obj.id:
                              ^^^^^^
AttributeError: 'NoneType' object has no attribute 'id'

Fix:

@pytest.mark.asyncio
async def test_good_log_create():
    state_user = dpytest.backend.get_state().user
    test_user = dpytest.backend.make_user("Test1", 2, id_num=386420570449051649)
    test_guild = dpytest.backend.make_guild(name="Test")
    test_channel = dpytest.backend.make_text_channel(guild=test_guild, name="Test")
    test_member = dpytest.backend.make_member(test_user, test_guild)
    test_member3 = dpytest.backend.make_member(state_user, test_guild)
    await dpytest.message("...", channel=test_channel, member=test_user)

I propose to somehow fix this in the documentation. Or make a method that will set the author when sending a message from the tested bot.

Sergeileduc commented 1 year ago

Hmmm, can't reproduce.

The error is `Dpytest backend not configured``, in your first test.

Anyway, this is some backend stuff, do it at your peril...

But what I can do is changing the code so the configure method, in the channels parameter, can accept either an int, or a list of strings, if you want to give special names to your channels (eg : ["Test",]) in your case.

Debianov commented 1 year ago

Hmmm, can't reproduce.

The error is `Dpytest backend not configured``, in your first test.

So did you run dpytest.configure? This is weird.

Sergeileduc commented 1 year ago

If I don't use configure at all, it gives me that.

    @pytest.mark.asyncio
    async def test_good_log_create():
>       test_user = dpytest.backend.make_user("Test1", 2, id_num=386420570449051649)

Pytest\test-github.py:8:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
..\..\.pyenv\pyenv-win\versions\3.10.5\lib\site-packages\discord\ext\test\backend.py:801: in make_user
    state = get_state()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def get_state() -> dstate.FakeState:
        """
            Get the current backend state, or raise an error if it hasn't been configured

        :return: Current backend state
        """
        if _cur_config is None:
>           raise ValueError("Dpytest backend not configured")
E           ValueError: Dpytest backend not configured

..\..\.pyenv\pyenv-win\versions\3.10.5\lib\site-packages\discord\ext\test\backend.py:549: ValueError

If I change your code to launch configure() (I could have done it using the fixture in the conftest.py, but anyways... :

The test pass in this case :

import discord
import discord.ext.commands as commands
import pytest
import pytest_asyncio
import discord.ext.test as dpytest
from discord.client import _LoopSentinel

@pytest_asyncio.fixture
async def bot(request):
    intents = discord.Intents.default()
    intents.members = True
    intents.message_content = True
    b = commands.Bot(command_prefix="!", intents=intents)
    # set up the loop
    if isinstance(b.loop, _LoopSentinel):
        await b._async_setup_hook()

    dpytest.configure(b)
    return b

@pytest.mark.asyncio
async def test_good_log_create(bot):
    test_user = dpytest.backend.make_user("Test1", 2, id_num=386420570449051649)
    test_guild = dpytest.backend.make_guild(name="Test")
    test_channel = dpytest.backend.make_text_channel(guild=test_guild, name="Test")
    test_member = dpytest.backend.make_member(test_user, test_guild)
    await dpytest.message("something that starts the execution of one of the discord.py functions containing send() in the body", channel=test_channel, member=test_user)
Debianov commented 1 year ago

The test pass in this case :

And now define some command via @bot.commands, which will send something in response via send and call this command via dpytest.message.

Sergeileduc commented 1 year ago

ok yeah, I can reproduce.

I won't investigate much on that, but you should consider investigating what "configure" does that you do not.