jamescooke/flake8-aaa

Provide a decorator for ignoring test functions/methods

Closed this issue ยท 4 comments

Usually flake8-aaa is extremely helpful and should be used. However there are rare test cases where it is more useful to turn the linting off.

I have never encountered more than a few test functions with such behaviour, so I think it would be a nice feature to turn off the linting for these functions.

Example for such a function: Testing a special character device:

def test_getc_ungetc():
    data = StringIO("abc\ndefg")
    context = FileContext(data)

    assert context.getc() == "a"
    assert context.getc() == "b"
    assert context._line == 0
    assert context._column == 2
    assert context.getc() == "c"
    assert context.getc() == "\n"
    assert context.getc() == "d"
    assert context._line == 1
    assert context._column == 1

    context.ungetc("d")
    context.ungetc("\n")

    assert context._column == 3
    assert context._line == 0
    assert context.getc() == "\n"

Expected Behaviour:

@flake8_aaa.ignore
def test_getc_ungetc():
      ....

This particular function does not get linted.

@jamescooke I would be happy to help.

Hi @daknuett - thanks for the issue ๐Ÿ‘

Regarding the idea of a decorator - I understand that not all tests should be linted all the time. The current work around for this situation is to mark the function with a # noqa comment. IMO that follows the approach used by flake8 which is to annotate the code with comments, rather than adding more code (which is what a decorator would be).

The hint for where the # noqa should be is in the error from flake8-aaa, which should look like:

test_example.py|48 col 1| AAA01 no Act block found in test

... which should point at the first character of the function definition.

Therefore:

def test_getc_ungetc():  # noqa

... should solve your problem.

However, please note that this will turn off all linters on that line - so bad things like this start to pass linting:

def test(x=1, 1):  # noqa

@daknuett Just looking at the example you've given above...

--- begin opinion ---

I think that this test example should be broken down into 1 fixture and 2 tests. Something like this:

@pytest.fixture
def context():
    data = StringIO("abc\ndefg")
    return FileContext(data)

def test_context(context):
    result = context

    assert result.getc() == "a"
    assert result.getc() == "b"
    assert result._line == 0
    assert result._column == 2
    assert result.getc() == "c"
    assert result.getc() == "\n"
    assert result.getc() == "d"
    assert result._line == 1
    assert result._column == 1

def test_getc_ungetc(context):
    result = context.ungetc("d"), context.ungetc("\n")

    assert result == (None, None)  # pins the return result of `ungetc()`
    assert context._column == 3
    assert context._line == 0
    assert context.getc() == "\n"

--- end opinion ---

For more detail / context, my post on extracting arrange code into fixtures could give you more info - feel free to ignore my opinion above as required ๐Ÿ˜Š

I'm going to close this issue now, but please feel free to comment / feedback.

Hi James,

At first: thank you for the solution. Although it does not look as nice as a decorator (I just like the way they look), it will do the job just fine.

By the way: using fixtures instead of big (and repeating) arrange code is something I do and recommend.

However your example would not work, because all the commands have to be executed in this very order. You can think of FileContext as a character device that lets you get one character or unget one character from/to the stream. You could bypass this problem by setting the fixture scope to "session", but then you would have to specify the execution order explicitly...

Let's just say, I have been unable to find a solution that is more elegant than my original solution.