views:

168

answers:

3

I do have several small modules where the tests are inside them and py.test or nose does not look for them because they do not contain test in their filename.

How can I convince py.test or nose to look for tests inside all python files, recursively - '''including the ones that do not have test in their filenames'''?

Inside the source files I do keep the standard naming convention: class testSomeName with methods def test_some_name.

If this is not possible, what other solution can I use to obtain the same result.

I do not want to manually create a list of all files containing the test, I want a solution that supports discovery.

+1  A: 

The documentation says that

By default all directories not starting with a dot are traversed, looking for test_*.py and *_test.py files. Those Python files are imported under their package name.

Can you ensure that this is the case with your code?

Update

(Caveat Emptor: I haven't tried/tested this) How about using the hooks provided for collecting directories and files?

py.test calls the following two fundamental hooks for collecting files and directories:

def pytest_collect_directory(path, parent):
    """ return Collection node or None for the given path. """

def pytest_collect_file(path, parent):
    """ return Collection node or None for the given path. """

Both return a collection node for a given path. All returned nodes from all hook implementations will participate in the collection and running protocol. The parent object is the parent node and may be used to access command line options via the parent.config object.

Manoj Govindan
I did RTFM and exactly this was the problem. I wanted to change this behavior, I do have unittests but they are all embedded in module file and called from __main__. I'm sure that in the future I will want to move them but for the moment I just want to scan for all of them, even in files not using this naming.
Sorin Sbarnea
@Sorin: updated my answer. See above.
Manoj Govindan
+3  A: 

You can also have a look at Nose which will discover tests without having to use a fixed file name convention.

You can bypass the regexp used to filter files in nose with the following code. Create a python module (i.e. my_nosetests.py)

import nose
from nose.plugins.base import Plugin

class ExtensionPlugin(Plugin):

    name = "ExtensionPlugin"

    def options(self, parser, env):
        Plugin.options(self,parser,env)

    def configure(self, options, config):
        Plugin.configure(self, options, config)
        self.enabled = True

    def wantFile(self, file):
        return file.endswith('.py')

    def wantDirectory(self,directory):
        return True

    def wantModule(self,file):
        return True


if __name__ == '__main__':
    includeDirs = ["-w", ".", ".."]
    nose.main(addplugins=[ExtensionPlugin()], argv=sys.argv.extend(includeDirs))

Now run my_nosetests.py as if you were running nosetests and you should have your tests running. Be aware that you are in fact loading all modules and searching for tests in them. Beware of any side effect of module loading.

Rod
nose is a great thing!
dmitko
Can you give a solution for nose, I would accept it!
Sorin Sbarnea
With nose you use nosetests in your root directory or pass the directory as an argument. By default it will recurse.
Rod
Rod, probablyyou did not fully read the question, my tests a not inside test*.py files.
Sorin Sbarnea
I will edit my answer with a code workaround
Rod
How do I specify a list of directories that I want to include in scanning, by default this example scans in `.` but I want to add other locations like, `../somedir/`. I know that there is the `-w` command line but I would like to be able to include this config option in this file and call it without any parameters.
Sorin Sbarnea
I have updated the code to do so. You should be able to go on from that. :)
Rod
I made the same approach, but with one remark, remove the second `-w` from the list. Now nosetest doesn't want several `-w`, it will pick all directories.
Sorin Sbarnea
+2  A: 

With py.test it's simple. Create a conftest.py file with this content:

# content of conftest.py file at root of your project
def pytest_collect_file(path, parent):
    if path.ext == ".py":
        return parent.Module(path, parent)

This will extend the collection process to create a test "Module" node for each ".py" file. Putting this into a conftest.py file makes it a project-specific extension which is automatically loaded if you type:

py.test 

For informational purposes you can also type:

py.test --collectonly

to see what tests and files are collected, example output:

<Directory 'morecollect'>
   <Module 'conftest.py'>
   <Directory 'pkg'>
     <Module 'test_x.py'>
        <Function 'test_hello2'>
   <Module 'x.py'>   # this is collected because of our conftest.py extension
     <Function 'test_hello'>

If needed you can also package the above conftest.py file as an installable plugin and make the extension available by installing the plugin. In this case you do not need any conftest.py file at all.

hpk42
For the moment I'm not able to test if this method works because py.test is crashing, see this bug: http://bitbucket.org/hpk42/py-trunk/issue/119/fail-to-impot-__init__py-internalerror
Sorin Sbarnea
Hey Sorin. Ok, Ronny and me just released a new version with a couple of fixes, including the issue you saw. Doing "easy_install -U py" should bring it to you. Thanks for reporting, have fun. Holger
hpk42