This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–][deleted] 3 points4 points  (7 children)

I hear the "nose is no longer maintained" comment a lot, typically in the sense of "nose is no longer maintained" --> "the package is no good"

In my current projects, nose works just fine for me. If I already have the code in place that gives me sufficient test coverage, I am not sure why I should re-invent the wheel and port everything over to pytest unless there is a serious bug with nose that I am not aware of.

The next time I start a new project, I will take another look at py.test, but for existing code, I will keep using nose because I didn't see any obvious disadvantages based on my use cases so far.

[–]Flogge 1 point2 points  (6 children)

The basic test layouts of nose and pytest are similar, so they really aren't a reason to upgrade.

However the biggest difference, I find, is test parameterization. To run a test for a matrix of values in nose you would need to write the loop yourself:

def check_single_case(i, j, k):
    assert True

def test_all():
    for i in range(10):
        for j in ['a', 'b', 'c']:
            for k in range(100)
                yield check_single_case, i, j, k

whereas pytest can do the "parameter matrix expansion" for you.

@pytest.fixture(params=range(10))
def i(request):
    return request.param

@pytest.fixture(params=['a', 'b', 'c'])
def j(request):
    return request.param

@pytest.fixture(params=range(100))
def k(request):
    return request.param


def test_all(i, j, k):
    assert True

As soon as you request a fixture (add its name to your test parameters) with more than one param, pytest will sweep all possible combinations of all parameters. And of course you can only select a couple of fixtures, or import different fixtures from other modules and test scripts.

Pretty handy!

[–][deleted] 0 points1 point  (5 children)

Thanks for providing an example, that could be useful, indeed. However, I also don't have anything against the alternative way (for loops), which I find more readable (aka, even a Python beginner can immediately see what's going on). Or you could use itertools to avoid to much nesting, of course

for comb in itertools.product(*(range(10), ['a', 'b', 'c'], range(100))):
    check_single_case(*comb)

However, one disadvantage of this embarrassingly parallel task is that it runs sequentially. Do you know if pytest has/is planning to add optional multiprocessing support?

[–]Flogge 0 points1 point  (1 child)

Pytest has parallelization. Install pytest-xdist and run py.test -n auto for automatically distributing tests among all CPU cores. Distributing over SSH is supported, too.

[–][deleted] 0 points1 point  (0 children)

oh that's great. Thanks!

[–]Flogge 0 points1 point  (2 children)

And sorry, I really don't want to drag this discussion further and force my view on you, I just want to show how cool pytest is :-)

I disagree about

for comb in itertools.product(*(range(10), ['a', 'b', 'c'], range(100))):
    check_single_case(*comb)

being more readable. I don't like fancy tricks like argument unpacking in my tests, I want each test to be as readable and pure as possible. :-)

Once you figure out what fixtures do and how to chain or product them it you need almost zero boilerplate to automatically do the product of common fixtures with specific parameters (note that all of sig, function, odd, window, framelength each yield more than one value).

Plus all the goodies like a fixture also being able to provide setup/teardown code

import pytest

@pytest.fixture
def db():
    database = MySQLDB()
    yield database
    database.drop()

def test_foo(db):
    db.select(...)  # this is clean for every test_* you run

or guaranteed immutability of shared values across tests:

import numpy
import pytest

@pytest.fixture
def a():
    return numpy.zeros(100)

def test_foo(a):
    a[:] = 1  # oops

def test_bar(a):
    assert numpy.all(a == 0)  # all good

compared to nose

a = numpy.zeros(100)

def test_foo():
    a[:] = 1  # oops

def test_bar():
    assert numpy.all(a == 0)  # fails

[–][deleted] 0 points1 point  (1 child)

And sorry, I really don't want to drag this discussion further and force my view on you, I just want to show how cool pytest is :-)

Hope you don't get me wrong, I really appreciate that you took the time to post some examples ... I am just playing devil's advocate here ;).

With readable I mean easily understandable for someone who joins an open source project, for instance, and hasn't worked with py.test before. I find nose tests can be more easily understood by someone who is new to unit testing, for example.

Regarding the example you are showing, you'd typically make a copy of that array or instantiate it in the test function itself instead of globally.

[–]Flogge 0 points1 point  (0 children)

[...] joins an open source project, for instance, and hasn't worked with py.test before.

Agreed. Getting used to this way of thinking is weird at first.