Mocks

Test mocks: they fake objects for testing.

Added in version 7.0.

class sopel.tests.mocks.MockIRCBackend(bot: AbstractBot)

Fake IRC connection backend for testing purpose.

Parameters:

bot (sopel.bot.Sopel) – a Sopel instance

This backend doesn’t require an actual connection. Instead, it stores every message sent in the message_sent list.

You can use the rawlist() function to compare the messages easily, and the clear_message_sent() method to clear previous messages.

Assuming you have a properly configured bot (i.e., an instance of Sopel with this fake backend), you can access its sent messages like this:

>>> from sopel.tests import rawlist
>>> bot.backend.irc_send(b'PRIVMSG #channel :Hi!\r\n')
>>> bot.backend.message_sent == rawlist('PRIVMSG #channel :Hi!')
True
>>> bot.backend.clear_message_sent()
[b'PRIVMSG #channel :Hi!\r\n']
>>> bot.backend.message_sent
[]

See also

The parent class contains all the methods that can be used on this test backend.

See also

The BotFactory automatically uses this fake backend when creating an instance of Sopel. As a result, it should be the preferred method of creating a test instance of Sopel with this fake connection backend.

clear_message_sent() list[bytes]

Clear and return previous messages sent.

Returns:

a copy of the cleared messages sent

Return type:

list

Added in version 7.1.

connected: bool

Convenient status flag.

Set to True to make the bot think it is connected.

irc_send(data: bytes) None

Store data into message_sent.

is_connected() bool

Tell if the backend is connected or not.

message_sent: list[bytes]

List of raw messages sent by the bot.

This list will be populated each time the irc_send() method is used: it will contain the raw IRC lines the bot wanted to send.

You can clear this list with the clear_message_sent() method, or use the rawlist() function to compare it.

on_irc_error(pretrigger: PreTrigger) None

Action to perform when the server sends an error event.

Parameters:

pretrigger – PreTrigger object with the error event

On IRC error, if bot.hasquit is set, the backend should close the connection so the bot can quit or reconnect as required.

run_forever() NoReturn

Run the backend forever (blocking call).

This method is responsible for initiating the connection to the server, and it must call bot.on_connect once connected, or bot.on_close if it fails to connect.

Upon successful connection, it must run forever, listening to the server and allowing the bot to use send_command() in a thread-safe way.

class sopel.tests.mocks.MockIRCServer(bot: Sopel, join_threads: bool = True)

Fake IRC Server that can send messages to a test bot.

Parameters:
  • bot – test bot instance to send messages to

  • join_threads – whether message functions should join running threads before returning (default: True)

This mock object helps developers when they want to simulate an IRC server sending messages to the bot (e.g., using its message() method or its more specific methods).

Changed in version 7.1: The join_threads parameter has been added.

See also

The IRCFactory factory can be used to create such a mock object, either directly or by using pytest and the ircfactory() fixture.

Important

This fake IRC server does not generate any network activity, and it does not react to anything the bot may send, as it is not an actual IRC server implementation.

Important

Sending messages to the bot may result in running threads: the bot uses threads to run triggers in parallel. This fake IRC server tries to join these threads after sending messages to the bot.

To provide for those rarer test cases where this behavior is not desirable, it can be controlled in two ways:

  • set join_threads to True (the default) to automatically join threads after sending messages

  • use the blocking optional argument of each method to override join_threads

Plugin authors should be wary of turning auto-join off, as this may result in unpredictable behaviors and flaky tests.

bot: Sopel

The bot instance used by the server to send messages.

Note

The bot instance should use a MockIRCBackend for testing.

channel_joined(
channel: str,
users: Iterable[str] | None = None,
*,
blocking: bool | None = None,
) None

Send events as if the bot just joined a channel.

Parameters:
  • channel – channel to send message for

  • users – list (or tuple) of nicknames that will be present in the RPL_NAMREPLY event

  • blocking – whether to block until all triggered threads have finished (optional)

This will send 2 messages to the bot:

  • a RPL_NAMREPLY event (353), giving information about users present in channel

  • a RPL_ENDOFNAMES event (366) for completion

Use this to emulate when the bot joins a channel, and the server replies with the list of connected users:

factory.channel_joined('#test', ['Owner', '@ChanServ'])

In this example, the bot will know that there are 2 other users present in #test: “Owner” (a regular user) and “ChanServ” (which is a channel operator). Note that the bot itself will be added to the list of users automatically, and you should not pass it in the users parameter.

This is particularly useful to populate the bot’s memory of who is in a channel.

If blocking is True, this method will wait to join all running triggers’ threads before returning. Setting it to False will skip this step. If not specified, the join_threads attribute will be obeyed.

Changed in version 7.1: The blocking parameter has been added.

Note

To add a user to a channel after using this method, you should use the join() method.

property chanserv: str

ChanServ’s message prefix.

invite(
user: MockUser,
nick: str,
channel: str,
*,
blocking: bool | None = None,
) None

Send events as if a user sent an INVITE.

Parameters:
  • user – the user sending the INVITE message

  • nick – the nick of the invited user

  • channel – where the nick is invited to

  • blocking – whether to block until all triggered threads have finished (optional)

This will send one event: an INVITE event from user to nick to join the given channel.

Use this to emulate when a user invites someone else to a channel:

factory.invite(MockUser('Owner'), 'Sopel' '#destination')

If blocking is True, this method will wait to join all running triggers’ threads before returning. Setting it to False will skip this step. If not specified, the join_threads attribute will be obeyed.

Added in version 8.1.

Note

To add the bot to a channel after using this method, you should use the channel_joined() method.

To add a different user to a channel after using this method, you should use the join() method.

join(
user: MockUser,
channel: str,
*,
blocking: bool | None = None,
) None

Send a channel JOIN event from user.

Parameters:
  • user – factory for the user who joins the channel

  • channel – channel the user joined

  • blocking – whether to block until all triggered threads have finished (optional)

This will send a JOIN message as if user just joined the channel:

factory.join(MockUser('NewUser'), '#test')

If blocking is True, this method will wait to join all running triggers’ threads before returning. Setting it to False will skip this step. If not specified, the join_threads attribute will be obeyed.

Changed in version 7.1: The blocking parameter has been added.

See also

This function is a shortcut to call the bot with the result from the user factory’s join() method.

join_threads: bool

Flag if the server should wait on running triggers.

The default join_threads behavior is suitable for testing most common plugin callables, and ensures that all callables dispatched by the bot in response to messages sent via this MockIRCServer are finished running before execution can continue.

If set to False, the mock server will not wait for the bot to finish processing threaded callables before returning.

Added in version 7.1.

Note

You can override join_threads on a per-method-call basis with the blocking arguments to the instance methods.

message(raw: str, *, blocking: bool | None = None) None

Send a raw message as if the bot received it.

Parameters:
  • raw – an IRC event from the server as seen by the bot

  • blocking – whether to block until all triggered threads have finished (optional)

This is a shortcut to calling Sopel’s on_message() method and dealing with running triggers’ threads.

It can be used with any messages (PRIVMSG, numeric events, etc.) the bot would receive from a server in order to test the bot and its plugins’ behavior.

If blocking is True, this method will wait to join all running triggers’ threads before returning. Setting it to False will skip this step. If not specified, the join_threads attribute will be obeyed.

Added in version 8.1.

mode_set(
channel: str,
flags: str,
users: Iterable[str],
*,
blocking: bool | None = None,
) None

Send a MODE event for a channel

Parameters:
  • channel – channel receiving the MODE event

  • flags – MODE flags set

  • users – users getting the MODE flags

  • blocking – whether to block until all triggered threads have finished (optional)

This will send a MODE message as if ChanServ added/removed channel modes for a set of users. This method assumes the flags parameter follows the IRC specification for MODE:

factory.mode_set('#test', '+vo-v', ['UserV', UserOP', 'UserAnon'])

If blocking is True, this method will wait to join all running triggers’ threads before returning. Setting it to False will skip this step. If not specified, the join_threads attribute will be obeyed.

Changed in version 7.1: The blocking parameter has been added.

pm(
user: MockUser,
text: str,
*,
blocking: bool | None = None,
) None

Send a PRIVMSG to the bot by a user.

Parameters:
  • user – factory for the user object who sends a message

  • text – content of the message sent to the bot

  • blocking – whether to block until all triggered threads have finished (optional)

This will send a PRIVMSG message as forwarded by the server for a user sending it to the bot:

factory.pm(MockUser('NewUser'), 'A private word.')

If blocking is True, this method will wait to join all running triggers’ threads before returning. Setting it to False will skip this step. If not specified, the join_threads attribute will be obeyed.

Changed in version 7.1: The blocking parameter has been added.

See also

This function is a shortcut to call the bot with the result from the user factory’s privmsg() method, using the bot’s nick as recipient.

say(
user: MockUser,
channel: str,
text: str,
*,
blocking: bool | None = None,
) None

Send a PRIVMSG to channel by user.

Parameters:
  • user – factory for the user who sends a message to channel

  • channel – recipient of the user’s PRIVMSG

  • text – content of the message sent to the channel

  • blocking – whether to block until all triggered threads have finished (optional)

This will send a PRIVMSG message as if user sent it to the channel, and the server forwarded it to its clients:

factory.say(MockUser('NewUser'), '#test', '.shrug')

If blocking is True, this method will wait to join all running triggers’ threads before returning. Setting it to False will skip this step. If not specified, the join_threads attribute will be obeyed.

Changed in version 7.1: The blocking parameter has been added.

See also

This function is a shortcut to call the bot with the result from the user’s privmsg() method.

class sopel.tests.mocks.MockUser(
nick: str | None = None,
user: str | None = None,
host: str | None = None,
)

Fake user that can generate messages to send to a bot.

Parameters:
  • nick (str) – nickname

  • user (str) – IRC username

  • host (str) – user’s host

The UserFactory factory can be used to create such mock object, either directly or by using pytest and the userfactory() fixture.

join(channel: str) str

Generate a JOIN command forwarded by the server for the user.

Parameters:

channel – channel the user joined

Returns:

the JOIN command the server sends to its clients present in the same channel when the user joins it.

property prefix: str

User’s hostmask as seen by other users on the server.

When the server forwards a User’s command, it uses this prefix.

privmsg(recipient: str, text: str) str

Generate a PRIVMSG command forwarded by a server for the user.

Parameters:
  • recipient – a channel name or the bot’s nick

  • text – content of the message

Returns:

a PRIVMSG command forwarded by the server as if it originated from the user’s hostmask