Add a hacking rule for assert_has_calls

Add the following hacking rule.

* N366: The assert_has_calls is a method rather than a variable.

  Not correct: mock_method.assert_has_calls = [mock.call(0)]
  Correct:     mock_method.assert_has_calls([mock.call(0)])

This patch is a follow-up patch for
Id094dd90efde09b9a835d4492f4a92b8f8ad296e.

Change-Id: I892f8c23ee44f2b3518776a9705e3543f3115cae
Signed-off-by: Takashi Natsume <takanattie@gmail.com>
This commit is contained in:
Takashi Natsume 2020-09-28 22:35:00 +09:00
parent 4acc112a30
commit 8d3c2ce92b
4 changed files with 43 additions and 0 deletions

View File

@ -67,6 +67,7 @@ Nova Specific Commandments
- [N363] Disallow ``(not_a_tuple)`` because you meant ``(a_tuple_of_one,)``.
- [N364] Check non-existent mock assertion methods and attributes.
- [N365] Check misuse of assertTrue/assertIsNone.
- [N366] The assert_has_calls is a method rather than a variable.
Creating Unit Tests
-------------------

View File

@ -127,6 +127,8 @@ mock_attribute_re = re.compile(r"[\.\(](retrun_value)[,=\s]")
# Regex for useless assertions
useless_assertion_re = re.compile(
r"\.((assertIsNone)\(None|(assertTrue)\((True|\d+|'.+'|\".+\")),")
# Regex for misuse of assert_has_calls
mock_assert_has_calls_re = re.compile(r"\.assert_has_calls\s?=")
class BaseASTChecker(ast.NodeVisitor):
@ -922,3 +924,18 @@ def useless_assertion(logical_line, filename):
match = useless_assertion_re.search(logical_line)
if match:
yield (0, msg % (match.group(2) or match.group(3)))
@core.flake8ext
def check_assert_has_calls(logical_line, filename):
"""Check misuse of assert_has_calls.
Not correct: mock_method.assert_has_calls = [mock.call(0)]
Correct: mock_method.assert_has_calls([mock.call(0)])
N366
"""
msg = "N366: The assert_has_calls is a method rather than a variable."
if ('nova/tests/' in filename and
mock_assert_has_calls_re.search(logical_line)):
yield (0, msg)

View File

@ -909,3 +909,27 @@ class HackingTestCase(test.NoDBTestCase):
self._assert_has_no_errors(
code, checks.useless_assertion,
filename="nova/tests/unit/test_context.py")
def test_check_assert_has_calls(self):
code = """
mock_method.assert_has_calls = [mock.call(1)]
mock_method2.assert_has_calls = [
mock.call(1), mock.call(2)]
"""
errors = [(x + 1, 0, 'N366') for x in range(2)]
# Check errors in 'nova/tests' directory.
self._assert_has_errors(
code, checks.check_assert_has_calls,
expected_errors=errors, filename="nova/tests/unit/test_context.py")
# Check no errors in other than 'nova/tests' directory.
self._assert_has_no_errors(
code, checks.check_assert_has_calls,
filename="nova/compute/api.py")
code = """
mock_method.assert_has_calls([mock.call(1)])
mock_method2.assert_has_calls([
mock.call(1), mock.call(2)])
"""
self._assert_has_no_errors(
code, checks.check_assert_has_calls,
filename="nova/tests/unit/test_context.py")

View File

@ -314,6 +314,7 @@ extension =
N363 = checks:did_you_mean_tuple
N364 = checks:nonexistent_assertion_methods_and_attributes
N365 = checks:useless_assertion
N366 = checks:check_assert_has_calls
paths =
./nova/hacking