Skip to content

Class scope in parametrized fixtures does not work if first class to be collected has only one method using that fixture #396

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

Closed
pytestbot opened this issue Nov 26, 2013 · 4 comments
Labels
type: bug problem that needs to be addressed

Comments

@pytestbot
Copy link
Contributor

Originally reported by: BitBucket: rajaram_s, GitHub: rajaram_s


When using class scope with parametrized fixtures, if the first class to be collected has only one method that uses that fixture, then the scope value is not honored. Instead objects are created and cleaned up for every test method execution as if the scope value was set to 'function'

#!python

# parametrized_fixtures.py

import pytest


class Person:
    population = 0

    def __init__(self, name):
        self.name = name
        print("\nNew Object for %s : %s" % (name, self))
        Person.population += 1

    def __del__(self):
        print 'Object for %s is deleted.' % self.name
        Person.population -= 1

    def getName(self):
        return self.name

    def sayHello(self):
        return 'Hello! my name is %s.' % self.name

    def count(self):
        return Person.population


@pytest.fixture(params=["John", "Doe"], scope="class")
def human(request):
    name = request.param
    human = Person(name)

    def finalizer():
        print("\nFinalizer called for %s" % human)
    request.addfinalizer(finalizer)
    return human


class TestGreetings:
    def test_hello(self, human):
        assert human.sayHello()

    #def test_bye(self, human):
    #    pass


class TestMetrics:
    def test_name_length(self, human):
        assert len(human.getName()) > 0

    def test_population_count(self, human):
        assert human.count() >= 0

When running the above code with the command py.test -s -v parametrized_fixtures.py the output is

#!python
=
platform linux2 -- Python 2.7.3 -- pytest-2.4.2 -- /usr/bin/python
plugins: xdist
collected 6 items

parametrized_fixtures.py:44: TestGreetings.test_hello[Doe]
New Object for Doe : <parametrized_fixtures.Person instance at 0x2492dd0>
PASSED
Finalizer called for <parametrized_fixtures.Person instance at 0x2492dd0>

parametrized_fixtures.py:52: TestMetrics.test_name_length[John]
New Object for John : <parametrized_fixtures.Person instance at 0x24994d0>
PASSED
Finalizer called for <parametrized_fixtures.Person instance at 0x24994d0>

parametrized_fixtures.py:52: TestMetrics.test_name_length[Doe]
New Object for Doe : <parametrized_fixtures.Person instance at 0x2499908>
PASSED
Finalizer called for <parametrized_fixtures.Person instance at 0x2499908>

parametrized_fixtures.py:55: TestMetrics.test_population_count[John]
New Object for John : <parametrized_fixtures.Person instance at 0x24998c0>
PASSED
Finalizer called for <parametrized_fixtures.Person instance at 0x24998c0>

parametrized_fixtures.py:55: TestMetrics.test_population_count[Doe]
New Object for Doe : <parametrized_fixtures.Person instance at 0x2499fc8>
PASSED
Finalizer called for <parametrized_fixtures.Person instance at 0x2499fc8>

parametrized_fixtures.py:44: TestGreetings.test_hello[John]
New Object for John : <parametrized_fixtures.Person instance at 0x2499950>
PASSED
Finalizer called for <parametrized_fixtures.Person instance at 0x2499950>


=======================6 passed in 0.02 seconds ======================
Object for John is deleted.
Object for Doe is deleted.
Object for John is deleted.
Object for Doe is deleted.
Object for John is deleted.
Object for Doe is deleted.

Instead** if the class 'TestGreetings' has 2 methods** that take the fixture 'human' as input argument,** then things work fine as expected**. For each object returned by the fixture function, all the test methods in a class get executed before repeating the same for the next object.

#!python
=
platform linux2 -- Python 2.7.3 -- pytest-2.4.2 -- /usr/bin/python
plugins: xdist
collected 8 items

parametrized_fixtures.py:44: TestGreetings.test_hello[John]
New Object for John : <parametrized_fixtures.Person instance at 0x2672ea8>
PASSED
parametrized_fixtures.py:47: TestGreetings.test_bye[John] PASSED
Finalizer called for <parametrized_fixtures.Person instance at 0x2672ea8>

parametrized_fixtures.py:44: TestGreetings.test_hello[Doe]
New Object for Doe : <parametrized_fixtures.Person instance at 0x2672f38>
PASSED
parametrized_fixtures.py:47: TestGreetings.test_bye[Doe] PASSED
Finalizer called for <parametrized_fixtures.Person instance at 0x2672f38>

parametrized_fixtures.py:52: TestMetrics.test_name_length[John]
New Object for John : <parametrized_fixtures.Person instance at 0x26724d0>
PASSED
parametrized_fixtures.py:55: TestMetrics.test_population_count[John] PASSED
Finalizer called for <parametrized_fixtures.Person instance at 0x26724d0>

parametrized_fixtures.py:52: TestMetrics.test_name_length[Doe]
New Object for Doe : <parametrized_fixtures.Person instance at 0x2672b48>
PASSED
parametrized_fixtures.py:55: TestMetrics.test_population_count[Doe] PASSED
Finalizer called for <parametrized_fixtures.Person instance at 0x2672b48>


======================== 8 passed in 0.03 seconds ========================
Object for John is deleted.
Object for Doe is deleted.
Object for John is deleted.
Object for Doe is deleted.

I observe the same issue when using the scope value with metafunc.parametrize and setting** indirect=True** to perform setup of a test during the test execution phase instead of test collection phase. I have attached the test file that shows the problem.

Also, I tried to debug using the source code and I think after the tests are collected, the function 'parametrize_sorted' is called which changes the order.


@pytestbot
Copy link
Contributor Author

Original comment by holger krekel (BitBucket: hpk42, GitHub: hpk42):


Confirmed, it's probably the sorting that is at fault -- which is somewhat intricate code, unfortunately. I could fix your issue by modifying that but it made some other tests fail so i am afraid it needs some careful analysis first.

@pytestbot
Copy link
Contributor Author

Original comment by holger krekel (BitBucket: hpk42, GitHub: hpk42):


refactor sorting wrt class-scopes. This fixes issue396 and also simplifies
the internal sorting algorithm a bit.

@pytestbot
Copy link
Contributor Author

Original comment by holger krekel (BitBucket: hpk42, GitHub: hpk42):


wrongly closed (although it's fixed i think, but see a next commit).

@pytestbot
Copy link
Contributor Author

Original comment by holger krekel (BitBucket: hpk42, GitHub: hpk42):


fix issue396 -- properly sort tests using class-scoped parametrization

also refix issue323 in a better way to avoid recursion for the fixture-grouping
algorithm alltogether.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug problem that needs to be addressed
Projects
None yet
Development

No branches or pull requests

1 participant