aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar MarkKoz <[email protected]>2020-03-02 09:42:00 -0800
committerGravatar MarkKoz <[email protected]>2020-03-02 11:38:39 -0800
commit28bcbf334eb08dfcd35b898b7cb803338664ee61 (patch)
treec111189b10b38c6271fe8d4b619cbe75299e0daf
parentAdding helpers to the Filtering whitelist (diff)
Add more pre-commit hooks
* Remove trailing whitespaces * Specify error code for a noqa in the free command
-rw-r--r--.pre-commit-config.yaml23
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--bot/cogs/free.py2
-rw-r--r--tests/README.md10
4 files changed, 27 insertions, 10 deletions
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 860357868..4bb5e7e1c 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,10 +1,27 @@
+exclude: ^\.cache/|\.venv/|\.git/|htmlcov/|logs/
repos:
-- repo: local
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v2.5.0
hooks:
- - id: flake8
+ - id: check-merge-conflict
+ - id: check-toml
+ - id: check-yaml
+ args: [--unsafe] # Required due to custom constructors (e.g. !ENV)
+ - id: end-of-file-fixer
+ - id: mixed-line-ending
+ args: [--fix=lf]
+ - id: trailing-whitespace
+ args: [--markdown-linebreak-ext=md]
+ - repo: https://github.com/pre-commit/pygrep-hooks
+ rev: v1.5.1
+ hooks:
+ - id: python-check-blanket-noqa
+ - repo: local
+ hooks:
+ - id: flake8
name: Flake8
description: This hook runs flake8 within our project's pipenv environment.
entry: pipenv run lint
language: python
types: [python]
- require_serial: true \ No newline at end of file
+ require_serial: true
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 39f76c7b4..61d11f844 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -43,7 +43,7 @@ To provide a standalone development environment for this project, docker compose
When pulling down changes from GitHub, remember to sync your environment using `pipenv sync --dev` to ensure you're using the most up-to-date versions the project's dependencies.
### Type Hinting
-[PEP 484](https://www.python.org/dev/peps/pep-0484/) formally specifies type hints for Python functions, added to the Python Standard Library in version 3.5. Type hints are recognized by most modern code editing tools and provide useful insight into both the input and output types of a function, preventing the user from having to go through the codebase to determine these types.
+[PEP 484](https://www.python.org/dev/peps/pep-0484/) formally specifies type hints for Python functions, added to the Python Standard Library in version 3.5. Type hints are recognized by most modern code editing tools and provide useful insight into both the input and output types of a function, preventing the user from having to go through the codebase to determine these types.
For example:
diff --git a/bot/cogs/free.py b/bot/cogs/free.py
index 02c02d067..33b55e79a 100644
--- a/bot/cogs/free.py
+++ b/bot/cogs/free.py
@@ -55,7 +55,7 @@ class Free(Cog):
msg = messages[seek - 1]
# Otherwise get last message
else:
- msg = await channel.history(limit=1).next() # noqa (False positive)
+ msg = await channel.history(limit=1).next() # noqa: B305
inactive = (datetime.utcnow() - msg.created_at).seconds
if inactive > TIMEOUT:
diff --git a/tests/README.md b/tests/README.md
index be78821bf..4f62edd68 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -83,7 +83,7 @@ TagContentConverter should return correct values for valid input.
As we are trying to test our "units" of code independently, we want to make sure that we do not rely objects and data generated by "external" code. If we we did, then we wouldn't know if the failure we're observing was caused by the code we are actually trying to test or something external to it.
-However, the features that we are trying to test often depend on those objects generated by external pieces of code. It would be difficult to test a bot command without having access to a `Context` instance. Fortunately, there's a solution for that: we use fake objects that act like the true object. We call these fake objects "mocks".
+However, the features that we are trying to test often depend on those objects generated by external pieces of code. It would be difficult to test a bot command without having access to a `Context` instance. Fortunately, there's a solution for that: we use fake objects that act like the true object. We call these fake objects "mocks".
To create these mock object, we mainly use the [`unittest.mock`](https://docs.python.org/3/library/unittest.mock.html) module. In addition, we have also defined a couple of specialized mock objects that mock specific `discord.py` types (see the section on the below.).
@@ -114,13 +114,13 @@ class BotCogTests(unittest.TestCase):
### Mocking coroutines
-By default, the `unittest.mock.Mock` and `unittest.mock.MagicMock` classes cannot mock coroutines, since the `__call__` method they provide is synchronous. In anticipation of the `AsyncMock` that will be [introduced in Python 3.8](https://docs.python.org/3.9/whatsnew/3.8.html#unittest), we have added an `AsyncMock` helper to [`helpers.py`](/tests/helpers.py). Do note that this drop-in replacement only implements an asynchronous `__call__` method, not the additional assertions that will come with the new `AsyncMock` type in Python 3.8.
+By default, the `unittest.mock.Mock` and `unittest.mock.MagicMock` classes cannot mock coroutines, since the `__call__` method they provide is synchronous. In anticipation of the `AsyncMock` that will be [introduced in Python 3.8](https://docs.python.org/3.9/whatsnew/3.8.html#unittest), we have added an `AsyncMock` helper to [`helpers.py`](/tests/helpers.py). Do note that this drop-in replacement only implements an asynchronous `__call__` method, not the additional assertions that will come with the new `AsyncMock` type in Python 3.8.
### Special mocks for some `discord.py` types
To quote Ned Batchelder, Mock objects are "automatic chameleons". This means that they will happily allow the access to any attribute or method and provide a mocked value in return. One downside to this is that if the code you are testing gets the name of the attribute wrong, your mock object will not complain and the test may still pass.
-In order to avoid that, we have defined a number of Mock types in [`helpers.py`](/tests/helpers.py) that follow the specifications of the actual Discord types they are mocking. This means that trying to access an attribute or method on a mocked object that does not exist on the equivalent `discord.py` object will result in an `AttributeError`. In addition, these mocks have some sensible defaults and **pass `isinstance` checks for the types they are mocking**.
+In order to avoid that, we have defined a number of Mock types in [`helpers.py`](/tests/helpers.py) that follow the specifications of the actual Discord types they are mocking. This means that trying to access an attribute or method on a mocked object that does not exist on the equivalent `discord.py` object will result in an `AttributeError`. In addition, these mocks have some sensible defaults and **pass `isinstance` checks for the types they are mocking**.
These special mocks are added when they are needed, so if you think it would be sensible to add another one, feel free to propose one in your PR.
@@ -144,7 +144,7 @@ Finally, there are some considerations to make when writing tests, both for writ
### Test coverage is a starting point
-Having test coverage is a good starting point for unit testing: If a part of your code was not covered by a test, we know that we have not tested it properly. The reverse is unfortunately not true: Even if the code we are testing has 100% branch coverage, it does not mean it's fully tested or guaranteed to work.
+Having test coverage is a good starting point for unit testing: If a part of your code was not covered by a test, we know that we have not tested it properly. The reverse is unfortunately not true: Even if the code we are testing has 100% branch coverage, it does not mean it's fully tested or guaranteed to work.
One problem is that 100% branch coverage may be misleading if we haven't tested our code against all the realistic input it may get in production. For instance, take a look at the following `member_information` function and the test we've written for it:
@@ -169,7 +169,7 @@ class FunctionsTests(unittest.TestCase):
If you were to run this test, not only would the function pass the test, `coverage.py` will also tell us that the test provides 100% branch coverage for the function. Can you spot the bug the test suite did not catch?
-The problem here is that we have only tested our function with a member object that had `None` for the `member.joined` attribute. This means that `member.joined.stfptime("%d-%m-%Y")` was never executed during our test, leading to us missing the spelling mistake in `stfptime` (it should be `strftime`).
+The problem here is that we have only tested our function with a member object that had `None` for the `member.joined` attribute. This means that `member.joined.stfptime("%d-%m-%Y")` was never executed during our test, leading to us missing the spelling mistake in `stfptime` (it should be `strftime`).
Adding another test would not increase the test coverage we have, but it does ensure that we'll notice that this function can fail with realistic data: