[TIP] py.test parameterized monkeypatch decorator

Lee Harr missive at hotmail.com
Wed Jul 3 14:17:17 PDT 2013


Recently I was bitten by an os.path bug related to windows vs posix paths.
(actually, an interaction between os.path and zipfile, but that's another story)
So now I've decided to learn more about testing :o)  After looking around for
a while I think I've settled on py.test

What I want to do is set up a decorator that will automatically run my
tests in each of [posixpath, ntpath] and I feel like I have a pretty nice
decorator working.


Here is some sample code:

#######
import os
import pytest
import ntpath
import posixpath


def splitdrive(fp):
    return os.path.splitdrive(fp)[0]


def for_all_os_path(f):
    dec = pytest.mark.parametrize(('path_module',), [
                                            (ntpath,), 
                                            (posixpath,)])

    import inspect
    sig = inspect.signature(f)
    
    if 'tmpdir' not in sig.parameters:
        @dec
        def fpatch_faop(monkeypatch, path_module, *args, **kw):
            monkeypatch.setattr(os, 'path', path_module)
            return f(*args, **kw)
        return fpatch_faop
    else:
        @dec
        def fpatch_faopt(monkeypatch, path_module, tmpdir, *args, **kw):
            monkeypatch.setattr(os, 'path', path_module)
            return f(tmpdir, *args, **kw)
        return fpatch_faopt


@for_all_os_path
def test_param():
    fp = 'foo/bar/baz.py'
    assert os.path.splitext(fp)[1] == '.py'



@for_all_os_path
def test_paths():
    fp = 'foo/bar/baz.py'
    assert os.path.splitext(fp)[1] == '.py'
    
    fp2 = 'c:\\foo\\bar\\baz.py'
    drive = splitdrive(fp2)
    if os.path is ntpath:
        assert  drive == 'c:'
    elif os.path is posixpath:
        assert drive == ''
    else:
        # os.path should be one of [ntpath, posixpath]
        assert False
        

@for_all_os_path
def test_splitdrive():
    fp = 'c:\\foo\\bar'
    drive = os.path.splitdrive(fp)[0]
    if os.path is ntpath:
        assert drive == 'c:'
    elif os.path is posixpath:
        assert drive == ''
    else:
        # os.path should be one of [posixpath, ntpath]
        assert False

@for_all_os_path
def test_tmpdir(tmpdir):
    fp = str(tmpdir.join('foo'))
    fd = open(fp, 'w')
    fd.write('test')
    fd.close()
    assert True

#######


Is there a better way to set up a test like this?

One issue I am running in to now is making sure that the returned paths
(the ones from my actual code) match the paths specified in the test.
(I would rather not have the "if os.path is ..." lines in all of my tests.)

One way I am doing that is to use normpath. ie:

@for_all_os_path
def test_related_dir_absolute():
    fp = '/foo/bar/baz.pyn'
    rel = _related_dir(fp)
    normpath = os.path.normpath
    assert normpath(rel) == normpath('/foo/bar/baz_pynd')


Does that look reliable? See a better way?


Thanks for any pointers! 		 	   		  


More information about the testing-in-python mailing list