Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add relative to other tests 'run' mark 'after' and 'before' kwargs #67

Closed
wants to merge 5 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 41 additions & 41 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
@@ -121,6 +121,47 @@ You can also use markers such as "first", "second", "last", and "second_to_last"
=========================== 4 passed in 0.02 seconds ===========================


Relative to other tests
-----------------------

Tests can be defined relative to others with "before" and "after" parameters.

.. code:: python

import pytest

@pytest.mark.run(after='test_second')
def test_third():
assert True

def test_second():
assert True

@pytest.mark.run(before='test_second')
def test_first():
assert True

::

$ py.test test_foo.py -vv
============================= test session starts ==============================
platform darwin -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- env/bin/python
plugins: ordering
collected 3 items

test_foo.py:11: test_first PASSED
test_foo.py:7: test_second PASSED
test_foo.py:4: test_third PASSED

=========================== 4 passed in 0.02 seconds ===========================



.. toctree::
:maxdepth: 2

.. _markers: https://pytest.org/latest/mark.html

Aspirational
============

@@ -204,44 +245,3 @@ By number

=========================== 4 passed in 0.02 seconds ===========================


Relative to other tests
-----------------------


.. code:: python

import pytest

@pytest.mark.run(after='test_second')
def test_third():
assert True

def test_second():
assert True

@pytest.mark.run(before='test_second')
def test_first():
assert True

::

$ py.test test_foo.py -vv
============================= test session starts ==============================
platform darwin -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- env/bin/python
plugins: ordering
collected 3 items

test_foo.py:11: test_first PASSED
test_foo.py:7: test_second PASSED
test_foo.py:4: test_third PASSED

=========================== 4 passed in 0.02 seconds ===========================



.. toctree::
:maxdepth: 2

.. _markers: https://pytest.org/latest/mark.html

70 changes: 69 additions & 1 deletion pytest_ordering/__init__.py
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
from ._version import __version__

import operator
import warnings

import pytest

@@ -47,7 +48,7 @@ def pytest_configure(config):


def pytest_collection_modifyitems(session, config, items):
grouped_items = {}
grouped_items, before_items, after_items = ({}, {}, {})

for item in items:

@@ -62,6 +63,16 @@ def pytest_collection_modifyitems(session, config, items):

if mark:
order = mark.kwargs.get('order')
if order is None:
before = mark.kwargs.get('before')
if before:
before_items.setdefault(before, []).append(item)
continue

after = mark.kwargs.get('after')
if after:
after_items.setdefault(after, []).append(item)
continue
else:
order = None

@@ -80,3 +91,60 @@ def pytest_collection_modifyitems(session, config, items):
sorted_items.extend([i[1] for i in end_list])

items[:] = [item for sublist in sorted_items for item in sublist]

def _get_item_index_by_name(item_name):
index = None
for i, item in enumerate(items):
if getattr(item, 'name') == item_name:
index = i
break
return index

for before_item_relative, _before_items in before_items.items():
index = _get_item_index_by_name(before_item_relative)
if index is not None:
for before_item in _before_items:
items.insert(index, before_item)
else:
if len(_before_items) == 1:
message_schema = "%s, indicated at parameter before of" \
+ " %s, doesn't exist"
message = message_schema % (before_item_relative,
_before_items[0].name)
else:
message_schema = "%s, indicated at parameter before of" \
+ " %s, doesn't exist"
test_names = ""
for i, before_item in enumerate(_before_items):
test_names += before_item.name
if i < len(_before_items) - 2:
test_names += ", "
elif i == len(_before_items) - 2:
test_names += " and "
message = message_schema % (before_item_relative, test_names)
warnings.warn(message, SyntaxWarning)
items.extend(_before_items)
for after_item_relative, _after_items in after_items.items():
index = _get_item_index_by_name(after_item_relative)
if index is not None:
for after_item in _after_items:
items.insert(index+1, after_item)
else:
if len(_after_items) == 1:
message_schema = "%s, indicated at parameter after of" \
+ " %s, doesn't exist"
message = message_schema % (after_item_relative,
_after_items[0].name)
else:
message_schema = "%s, indicated at parameter after of" \
+ " %s, doesn't exist"
test_names = ""
for i, after_item in enumerate(_after_items):
test_names += after_item.name
if i < len(_after_items) - 2:
test_names += ", "
elif i == len(_after_items) - 2:
test_names += " and "
message = message_schema % (after_item_relative, test_names)
warnings.warn(message, SyntaxWarning)
items.extend(_after_items)
67 changes: 67 additions & 0 deletions tests/test_ordering.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import re
import warnings

import pytest

@@ -268,6 +269,72 @@ def test_5(self): pass
assert item_names_for(tests_content) == ['test_3', 'test_4', 'test_5', 'test_1', 'test_2']


def test_relative_to_other_tests(item_names_for):
tests_content = """
import pytest

@pytest.mark.run(after='test_2')
def test_3(): pass

def test_2(): pass

@pytest.mark.run(before='test_2')
def test_1(): pass
"""
assert item_names_for(tests_content) == ['test_1', 'test_2', 'test_3']

def test_relative_to_other_invalid_tests(item_names_for):
tests_content = """
import pytest

@pytest.mark.run(before='test_A')
def test_1(): pass

def test_2(): pass

@pytest.mark.run(after='test_B')
def test_3(): pass

@pytest.mark.run(before='test_C')
def test_4(): pass

@pytest.mark.run(before='test_C')
def test_5(): pass

@pytest.mark.run(before='test_C')
def test_6(): pass

@pytest.mark.run(after='test_D')
def test_7(): pass

@pytest.mark.run(after='test_D')
def test_8(): pass
"""

with warnings.catch_warnings(record=True) as catched_warnings:
expected_item_names = [
'test_2', 'test_1', 'test_4', 'test_5',
'test_6', 'test_3', 'test_7', 'test_8',
]
for item_name in item_names_for(tests_content):
assert item_name in expected_item_names

expected_warning_messages = [
"test_A, indicated at parameter before of test_1, doesn't exist",
"test_B, indicated at parameter after of test_3, doesn't exist",
"test_C, indicated at parameter before of test_4, test_5 and test_6, doesn't exist",
"test_D, indicated at parameter after of test_7 and test_8, doesn't exist",
]

n_other_warnings = 0
for w in catched_warnings:
if not issubclass(w.category, SyntaxWarning):
n_other_warnings += 1
continue
assert w.message.__str__() in expected_warning_messages
assert len(expected_warning_messages) + n_other_warnings == len(catched_warnings)


def test_markers_registered(capsys):
pytest.main(['--markers'])
out, err = capsys.readouterr()