| Commit message (Collapse) | Author | Age | Lines |
... | |
| | |/
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
The fix is to mock the loop and pass it to the Bot. It will then set
it as `self.loop` rather than trying to get an event loop from asyncio.
The `create_task` patch has been moved to this loop mock rather than
being done in MockBot to ensure that it applies to anything calling it
when instantiating the Bot.
|
| |\| |
|
| | | |
|
| | | |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
The original approach of messing with the `attribute_name` didn't work
for reasons I won't discuss here (would require knowledge of patcher
internals). The new approach doesn't use patch.multiple but mimics it by
applying multiple patch decorators to the function. As a consequence,
this can no longer be used as a context manager.
|
| | |
| | |
| | |
| | |
| | | |
This gives the caller more flexibility. Sometimes attribute names are
too long or they don't follow a naming scheme accepted by the linter.
|
| |/
|/|
| |
| |
| |
| | |
This helper reduces redundancy/boilerplate by setting default values.
It also has the consequence of shortening the length of the invocation,
which makes it faster to use and easier to read.
|
| | |
|
|/ |
|
|
|
|
| |
The test suite for the new role/member syncers used the "old"-style test suite with the helpers implemented for Python 3.7. I have migrated it to use the new Python 3.8 asyncio test helpers.
|
|\ |
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
This error is due to the use of an actual instance of APIClient as the
spec for the mock. recreate() is called in __init__ which in turn
creates a task for the _create_session coroutine.
The approach to the solution is to use the type for the spec rather than
and instance, thus avoiding any call of __init__. However, without an
instance, instance attributes will not be included in the spec.
Therefore, they are defined as class attributes on the actual APIClient
class definition and given default values.
Alternatively, a subclass of APIClient could have been made in the
tests.helpers module to define those class attributes. However, it
seems easier to maintain if the attributes are in the original class
definition.
|
| |
| |
| |
| |
| | |
Instances of discord.Colour and discord.Permissions will be created
by default or when ints are given as values for those attributes.
|
| | |
|
| | |
|
| |
| |
| |
| |
| |
| | |
Python 3.8 introduced an `unittest.mock.AsyncMock` class that can be used to mock coroutines and other types of asynchronous operations like async iterators and async context managers. As we were using our custom, but limited, AsyncMock, I have replaced our mock with unittest's AsyncMock.
Since Python 3.8 also introduces a different way of automatically detecting which attributes should be mocked with an AsyncMock, I've changed our CustomMockMixin to use this new method as well. Together with a couple other small changes, this means that our Custom Mocks now use a lazy method of detecting coroutine attributes, which significantly speeds up the test suite.
|
|/
|
|
| |
Since we upgraded to Python 3.8, we can now use the new IsolatedAsyncioTestCase test class to use coroutine-based test methods instead of our own, custom async_test decorator. I have changed the base class for all of our test classes that use coroutine-based test methods and removed the now obsolete decorator from our helpers.
|
| |
|
|
|
|
|
|
|
| |
I have added a mock type to mock `discord.Webhook` instances. Note
that the current type is specifically meant to mock webhooks that
use an AsyncAdaptor and therefore has AsyncMock/coroutine mocks for
the "maybe-coroutine" methods specified in the `discord.py` docs.
|
|
|
|
|
| |
The new AsyncIteratorMock no longer needs an additional method to be
used with a Mock object.
|
|
|
|
|
|
|
|
| |
I have added a special mock that follows the specifications of a
`discord.User` instance. This is useful, since `Users` have less
attributes available than `discord.Members`. Since this difference
in availability of information can be important, we should not use
a `MockMember` to mock a `discord.user`.
|
|
|
|
|
|
|
|
|
|
|
| |
The AsyncIteratorMock included in Python 3.8 will work similarly to
the mocks of callabes. This means that it allows you to set the items
it will yield using the `return_value` attribute. It will also have
support for the common Mock-specific assertions.
This commit introduces some backports of those features in a slightly
simplified way to make the transition to Python 3.8 easier in the
future.
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
As stated from the start, our intention is to add custom mock types
as we need them for testing. While writing tests for DuckPond, I
noticed that we did not have a mock type for Attachments, so I added
one with this commit.
In addition, I think it's a very sensible for MockMessage to have an
empty list as a default value for the `attachements` attribute. This
is equal to what `discord.Message` returns for a message without
attachments and makes sure that if you don't explicitely add an
attachment to a message, `MockMessage.attachments` tests as falsey.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
By default, a mocked value is considered `truthy` in Python, like all
non-empty/non-zero/non-None values in Python. This means that if an
attribute is not explicitly set on a mock, it will evaluate at as
truthy in a boolean context, since the mock will provide a truthy
mocked value by default.
This is not the best default value for the `bot` attribute of our
MockMember type, since members are rarely bots. It makes much more
intuitive sense to me to consider a member to not be a bot, unless we
explicitly set `bot=True`.
This commit sets that sensible default value that can be overwritten
by passing `bot=False` to the constructor or setting the `object.bot`
attribute to `False` after the creation of the mock.
|
|\ |
|
| |
| |
| |
| |
| |
| |
| | |
Previously, the coroutine object passed to `MockBot.loop.create_task`
would trigger a `RuntimeWarning` for not being awaited as we do not
actually create a task for it. To prevent these warnings, coroutine
objects passed will now automatically be closed.
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Our custom `discord.py` now follow the specifications of the object
they are mocking more strictly by using the `spec_set` instead of the
`spec` kwarg to initialize the specifications. This means that trying
to set an attribute that does not follow the specifications will now
also result in an `AttributeError`.
To make sure we are not trying to set illegal attributes during the
default initialization of the mock objects, I've changed the way we
handle default values of parameters. This does introduce a breaking
change: Instead of passing a `suffix_id`, the `id` attribute should
now be passed using the exact name. `id`.
This commit also makes sure existing tests follow this change.
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
The `name` keyword argument has a special meaning for the default
mockobjects provided by `unittest.mock`. This means that by default,
the common d.py `name` attribute can't be set during initalization of
one of our custom Mock-objects by passing it to the constructor.
Since it's unlikely for us to make use of the special `name` feature
of mocks and more likely to want to set the d.py `name` attribute, I
added special handling of the `name` kwarg.
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Previously, logging messages would output to std.out. when running
individual test files (instead of running the entire suite). To
prevent this, I've added a `for`-loop to `tests.helpers` that sets
the level of all registered loggers to `CRITICAL`.
The reason for adding this to `tests.helpers` is simple: It's the
most common file to be imported in individual tests, increasing the
chance of the code being run for individual test files.
A small downside of this way of handling logging is that when we are
trying to assert logging messages are being emitted, we need to set
the logger explicitly in the `self.assertLogs` context manager. This
is a small downside, though, and probably good practice anyway.
There was one test in `tests.bot.test_api` that did not do this, so
I have changed this to make the test compatible with the new set-up.
|
|/ |
|
|
|
|
|
|
|
|
|
|
| |
I accidentally forgot to update the docstring of `CustomMockMixin`,
which changed quite dramatically in scope with the last commit. This
commit remedies that.
In addition, I inadvertently forgot to remove the `child_mock_type`
class attribute from `MockRole`. Since it uses the default value, it
is no longer necessary to specify it in the child class as well.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
I have enhanced the custom mocks defined in `tests/helpers.py` in a
couple of important ways.
1. Automatically create AsyncMock attributes using `inspect`
Our previous approach, hard-coding AsynckMock attributes for all the
coroutine function methods defined for the class we are trying to
mock is prone to human error and not resilient against changes
introduced in updates of the library we are using.
Instead, I have now created a helper method in our `CustomMockMixin`
(formerly `GetChildMockMixin`) that automatically inspects the spec
instance we've passed for `coroutine functions` using the `inspect`
module. It then sets the according attributes with instances of the
AsyncMock class.
There is one caveat: `discord.py` very rarely defines regular methods
that return a coroutine object. Since the returned coroutine should
still be awaited, these regular methods should also be mocked with an
AsyncMock. However, since they are regular methods, `inspect` does
not detect them and they have to be added manually. (The only case of
this I've found so far is `Client.wait_for`.)
2. Properly set special attributes using `kwargs.get`
As we want attributes that point to other discord.py objects to use
our custom mocks (.e.g, `Message.author` should use `MockMember`),
the `__init__` method of our custom mocks make sure to correctly
instantiate these attributes.
However, the way we previously did that means we can't instantiate
the custom mock with a mock instance we provide, since this special
instantiation would overwrite the custom object we'd passed. I've
solved this by using `kwargs.get`, with a new mock as the default
value. This makes sure we only create a new mock if we didn't pass
a custom one:
```py
class MockMesseage:
def __init__(self, **kwargs):
self.author = kwargs.get('author', MockMember())
```
As you can see, we will only create a new MockMember if we did not
pass an `author` argument.
3. Factoring out duplicate lines
Since our `CustomMockMixin` is a parent to all of our custom mock
types, it makes sense to use it to factor out common code of all of
our custom mocks.
I've made the following changes:
- Set a default child mock type in the mixin.
- Create an `__init__` that takes care of the `inspect` of point 1
This means we won't have to repeat this in all of the child classes.
4. Three new Mock types: Emoji, PartialEmoji, and Reaction
I have added three more custom mocks:
- MockEmoji
- MockPartialEmoji
- MockReaction
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
- https://docs.python.org/3/library/unittest.mock.html
We previously used an override of the `__new__` method to prevent our
custom mock types from instantiating their children with their own
type instead of a general mock type like `MagicMock` or `Mock`.
As it turns out, the Python documentation suggests another method of
doing this that does not involve overriding `__new__`. This commit
implements this new method to make sure we're using the idiomatic way
of handling this.
The suggested method is overriding the `_get_child_mock` method in
the subclass. To make our code DRY, I've created a mixin that should
come BEFORE the mock type we're subclassing in the MRO.
---
In addition, I have also added this new mixin to our `AsyncMock`
class to make sure that its `__call__` method returns a proper mock
object after it has been awaited. This makes sure that subsequent
attribute access on the returned object is mocked as expected.
|
|
|
|
|
|
|
| |
This commit replaces the standard MagicMocks by our specialized mocks
for discord.py objects. It also adds the missing `channel` attribute
to the `tests.helpers.MockMessage` mock and moves the file to the
correct folder.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This commit introduces some new Mock-types to the already existing
Mock-types for discord.py objects. The total list is now:
- MockGuild
- MockRole
- MockMember
- MockBot
- MockContext
- MockTextChannel
- MockMessage
In addition, I've added all coroutines in the documentation for these
discord.py objects as `AsyncMock` attributes to ease testing. Tests
ensure that the attributes set for the Mocks exist for the actual
discord.py objects as well.
|
|
|
|
|
|
|
|
|
| |
I have change the testrunner from `unittest` to `xmlrunner` in the
Azure pipeline to be able to publish our test results on Azure. This
is the same runner as `site` uses to generate XML reports.
In addition, I've cleaned up some small mistakes in docstrings and
`README.md`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
After a discussion in the core developers channel, we have decided to
migrate from `pytest` to `unittest` as the testing framework. This
commit sets up the repository to use `unittest` and migrates the
first couple of tests files to the new framework.
What I have done to migrate to `unitest`:
- Removed all `pytest` test files, since they are incompatible.
- Removed `pytest`-related dependencies from the Pipfile.
- Added `coverage.py` to the Pipfile dev-packages and relocked.
- Added convenience scripts to Pipfile for running the test suite.
- Adjust to `azure-pipelines.yml` to use `coverage.py` and `unittest`.
- Migrated four test files from `pytest` to `unittest` format.
In addition, I've added five helper Mock subclasses in `helpers.py`
and created a `TestCase` subclass in `base.py` to add an assertion
that asserts that no log records were logged within the context of
the context manager. Obviously, these new utility functions and
classes are fully tested in their respective `test_` files.
Finally, I've started with an introductory guide for writing tests
for our bot in `README.md`.
|
| |
|
| |
|
|
|