logo

Pytest Filter

← Back to Filter List

Pytest


Runs the tests in the specified Python modules. Python modules must be installed on the system. Returns a key-value store with test results and source code. Many packages are installed without tests, so this won't work.

Aliases for this filter

  • pytest

Converts from file formats:

  • .txt
  • .py

To file formats:

  • .sqlite3
  • .json

Available settings:

SettingDescriptionDefault
add-new-filesBoolean or list of extensions/patterns to match.False
added-in-versionDexy version when this filter was first available.
additional-doc-filtersFilters to apply to additional documents created as side effects.{}
additional-doc-settingsSettings to apply to additional documents created as side effects.{}
chdirPath from package dir to chdir to.None
data-typeAlias of custom data class to use to store filter output.keyvalue
error-on-import-failShould an exception be raised if importing a specified module or file fails?False
examplesTemplates which should be used as examples for this filter.[]
exclude-add-new-filesList of patterns to skip even if they match add-new-files.[]
exclude-new-files-from-dirList of directories to skip when adding new files.[]
extFile extension to output.None
extension-mapDictionary mapping input extensions to default output extensions.None
helpHelpstring for plugin.Runs the tests in the specified Python modules. Python modules must be installed on the system. Returns a key-value store with test results and source code. Many packages are installed without tests, so this won't work.
input-extensionsList of extensions which this filter can accept as input.[u'.txt', u'.py']
keep-originalsWhether, if additional-doc-filters are specified, the original unmodified docs should also be added.False
load-tests-mode'dir' to find tests by location relative to package dir, 'name' if tests are in their own module.dir
mkdirA directory which should be created in working dir.None
mkdirsA list of directories which should be created in working dir.[]
nodocWhether filter should be excluded from documentation.False
nose-argvNeed to fake out argv since sys.argv will be args for dexy, not nose.[u'nosetests', u'--stop', u'--verbose']
outputWhether to output results of this filter by default by reporters such as 'output' or 'website'.False
output-extensionsList of extensions which this filter can produce as output.[u'.sqlite3', u'.json']
override-workspace-exclude-filtersIf True, document will be populated to other workspaces ignoring workspace-exclude-filters.False
path-to-tests-dirPath from package dir to tests dir.../tests
preserve-prior-data-classWhether output data class should be set to match the input data class.False
require-outputShould dexy raise an exception if no output is produced by this filter?True
run-testsWhether to run tests or just return test source code.True
tagsTags which describe the filter.[]
test-passed-when-not-runValue which should be used for test_passed when tests are not run.True
variablesA dictionary of variable names and values to make available to this filter.{}
varsA dictionary of variable names and values to make available to this filter.{}
workspace-exclude-filtersFilters whose output should be excluded from workspace.[u'pyg']
workspace-includesIf set to a list of filenames or extensions, only these will be populated to working dir.None
Filter Source Code
class PythonTest(PythonIntrospection):
    """
    Runs the tests in the specified Python modules.

    Python modules must be installed on the system. Returns a key-value store
    with test results and source code.
    
    Many packages are installed without tests, so this won't work.
    """
    aliases = ['pytest']
    _settings = {
            'run-tests' : (
                "Whether to run tests or just return test source code.",
                True
                ),
            'test-passed-when-not-run' : (
                "Value which should be used for test_passed when tests are not run.",
                True
                ),
            'load-tests-mode' : (
                "'dir' to find tests by location relative to package dir, 'name' if tests are in their own module.",
                'dir'),
            'path-to-tests-dir' : (
                "Path from package dir to tests dir.",
                "../tests"),
            'chdir' : (
                "Path from package dir to chdir to.",
                None),
            'nose-argv' : (
                "Need to fake out argv since sys.argv will be args for dexy, not nose.",
                ['nosetests', '--stop', '--verbose'])
            }

    def is_active(self):
        return AVAILABLE

    def load_tests_from_dir(self, module_name):
        self.log_debug("Loading module '%s' to find its tests." % module_name)
        mod = self.load_module(module_name)

        self.mod_file_dir = os.path.dirname(mod.__file__)
        relpath = self.setting('path-to-tests-dir')
        tests_dir = os.path.normpath(os.path.join(self.mod_file_dir, relpath))
        self.log_debug("Attempting to load tests from dir '%s'" % tests_dir)

        loader = nose.loader.TestLoader()
        return loader.loadTestsFromDir(tests_dir)

    def load_tests_from_name(self, module_name):
        loader = nose.loader.TestLoader()
        return loader.loadTestsFromName(module_name)

    def load_tests(self, module_name):
        mode = self.setting('load-tests-mode')
        if mode == 'dir':
            return self.load_tests_from_dir(module_name)
        elif mode == 'name':
            return self.load_tests_from_name(module_name)
        else:
            msg = "Invalid load-tests-mode setting '%s'" % mode
            raise dexy.exceptions.UserFeedback(msg)

    def append_source(self, test, test_passed):
        for key in dir(test.context):
            member = test.context.__dict__[key]

            if inspect.ismethod(member) or inspect.isfunction(member):
                qualified_test_name = "%s.%s" % (test.context.__name__, member.__name__)
                source = inspect.getsource(member.__code__)

                if member.func_doc:
                    doc = inspect.cleandoc(member.func_doc)
                    self.output_data.append("%s:doc" % qualified_test_name, doc)

                comments = inspect.getcomments(member.__code__)

                self.output_data.append("%s:source" % qualified_test_name, source)
                self.output_data.append("%s:name" % qualified_test_name, member.func_name)
                self.output_data.append("%s:comments" % qualified_test_name, comments)
                self.output_data.append("%s:passed" % qualified_test_name, unicode(test_passed))

    def run_test(self, test):
        # TODO This isn't working... maybe because we're running this in a test
        noselogs = StringIO.StringIO()
        config = nose.config.Config(
                logStream = noselogs
                )

        if self.setting('run-tests'):
            self.log_debug("Running test suite %s" % test)
            test_passed = nose.core.run(
                    suite=test,
                    config=config,
                    argv=self.setting('nose-argv')
                    )
            self.log_debug("Passed: %s" % test_passed)
        else:
            test_passed = self.setting('test-passed-when-not-run')

        return test_passed

    def process(self):
        module_names = unicode(self.input_data).split()
        self.mod_file_dir = None
        orig_wd = os.path.abspath(".")
        chdir = self.setting('chdir')

        for module_name in module_names:
            tests = self.load_tests(module_name)

            if chdir:
                chdir = os.path.abspath(os.path.join(self.mod_file_dir, chdir))
                self.log_warn("Changing dir to %s for tests" % chdir)
                os.chdir(chdir)

            for test in tests:
                test_passed = self.run_test(test)
                self.append_source(test, test_passed)

            if chdir:
                self.log_warn("Changing dir back to %s" % orig_wd)
                os.chdir(orig_wd)

        self.output_data.save()

Content © 2013 Dr. Ana Nelson | Site Design © Copyright 2011 Andre Gagnon | All Rights Reserved.