سؤال

لدي دليل يحتوي على اختبارات وحدة بيثون الخاصة بي. كل وحدة اختبار الوحدة هي من النموذج اختبار _ *.. وبعد أنا أحاول جعل ملف يسمى all_test.py. هذا سوف، لقد خمنت ذلك، تشغيل جميع الملفات في نموذج الاختبار المذكور أعلاه وإرجاع النتيجة. لقد حاولت طريقتين حتى الآن؛ كلاهما فشل. سأظهر الطريقتين، وآمل أن يعرف شخص ما هناك كيفية القيام بذلك بشكل صحيح بشكل صحيح.

لمحاولة My Valiant الخاصة بي، فكرت "إذا قمت باستيراد جميع وحدات الاختبار الخاصة بي في الملف، ثم اتصل بهذا unittest.main() دوداد، وسوف تعمل، أليس كذلك؟ "حسنا، اتضح أنني كنت مخطئا.

import glob
import unittest

testSuite = unittest.TestSuite()
test_file_strings = glob.glob('test_*.py')
module_strings = [str[0:len(str)-3] for str in test_file_strings]

if __name__ == "__main__":
     unittest.main()

هذا لم ينجح، والنتيجة التي حصلت عليها كانت:

$ python all_test.py 

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

لتجربتي الثانية، أنا على الرغم من، حسنا، ربما سأحاول القيام بهذا الاختبار كله بطريقة أكثر "دليل". لذلك حاولت أن أفعل ذلك أدناه:

import glob
import unittest

testSuite = unittest.TestSuite()
test_file_strings = glob.glob('test_*.py')
module_strings = [str[0:len(str)-3] for str in test_file_strings]
[__import__(str) for str in module_strings]
suites = [unittest.TestLoader().loadTestsFromName(str) for str in module_strings]
[testSuite.addTest(suite) for suite in suites]
print testSuite 

result = unittest.TestResult()
testSuite.run(result)
print result

#Ok, at this point I have a result
#How do I display it as the normal unit test command line output?
if __name__ == "__main__":
    unittest.main()

هذا أيضا لم ينجح، ولكن يبدو قريبا جدا!

$ python all_test.py 
<unittest.TestSuite tests=[<unittest.TestSuite tests=[<unittest.TestSuite tests=[<test_main.TestMain testMethod=test_respondes_to_get>]>]>]>
<unittest.TestResult run=1 errors=0 failures=0>

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

يبدو أن لدي مجموعة من نوع ما، ويمكنني تنفيذ النتيجة. أنا قلق قليلا بشأن حقيقة أنه يقول لدي فقط run=1, ، يبدو أن هذا ينبغي أن يكون run=2, ، لكنه تقدم. ولكن كيف يمكنني المرور وعرض النتيجة إلى الرئيسية؟ أو كيف يمكنني العمل بشكل أساسي حتى أتمكن من تشغيل هذا الملف، وفي القيام بذلك، قم بتشغيل جميع اختبارات الوحدة في هذا الدليل؟

هل كانت مفيدة؟

المحلول

يمكنك استخدام عداء اختبار من شأنه أن يفعل ذلك من أجلك. أنف جيد جدا على سبيل المثال. عند تشغيل، ستجد الاختبارات في الشجرة الحالية وتشغيلها.

محدث:

إليك بعض الكود من أيام ما قبل الأنف. ربما لا تريد أن تكون قائمة أسماء الوحدة النمطية الصريحة، ولكن ربما تكون الباقي مفيدة لك.

testmodules = [
    'cogapp.test_makefiles',
    'cogapp.test_whiteutils',
    'cogapp.test_cogapp',
    ]

suite = unittest.TestSuite()

for t in testmodules:
    try:
        # If the module defines a suite() function, call it to get the suite.
        mod = __import__(t, globals(), locals(), ['suite'])
        suitefn = getattr(mod, 'suite')
        suite.addTest(suitefn())
    except (ImportError, AttributeError):
        # else, just load all the test cases from the module.
        suite.addTest(unittest.defaultTestLoader.loadTestsFromName(t))

unittest.TextTestRunner().run(suite)

نصائح أخرى

مع Python 2.7 وأعلى، لا تضطر إلى كتابة رمز جديد أو استخدام أدوات الطرف الثالث للقيام بذلك؛ تنفيذ الاختبار العريض عبر سطر الأوامر مدمج.

python -m unittest discover <test_directory>
# or
python -m unittest discover -s <directory> -p '*_test.py'

يمكنك قراءة المزيد في بيثون 2.7.أو بيثون 3.x. التوثيق التغريد.

هذا ممكن الآن مباشرة من Unittest: unittest.tliader.discover..

import unittest
loader = unittest.TestLoader()
start_dir = 'path/to/your/test/files'
suite = loader.discover(start_dir)

runner = unittest.TextTestRunner()
runner.run(suite)

في بيثون 3، إذا كنت تستخدم unittest.TestCase:

  • يجب أن يكون لديك فارغة (أو غير ذلك) __init__.py ملف في الخاص بك test الدليل (يجب اسمه test/)
  • ملفات الاختبار الخاصة بك في الداخل test/ تطابق النمط test_*.py. وبعد يمكن أن تكون داخل الدليل الفرعي تحت test/, ، ويمكن تذكار تلك الدفاتر كأي شيء.

ثم، يمكنك تشغيل جميع الاختبارات مع:

python -m unittest

منتهي! حل أقل من 100 خطوط. نأمل أن يكون بيثون مبتدئ آخر يوفر الوقت من خلال العثور على هذا.

جيدا من خلال دراسة الرمز أعلاه قليلا (على وجه التحديد باستخدام TextTestRunner و defaultTestLoader)، كنت قادرا على الحصول على قريبة جدا. في النهاية، قمت بإصلاح التعليمات البرمجية الخاصة بي من خلال إزارة جميع أجنحة الاختبار أيضا إلى منشئ أجنحة واحدة، بدلا من إضافةها "يدويا"، والتي تم إصلاح مشكلاتي الأخرى. لذلك هنا هو الحل بلدي.

import glob
import unittest

test_files = glob.glob('test_*.py')
module_strings = [test_file[0:len(test_file)-3] for test_file in test_files]
suites = [unittest.defaultTestLoader.loadTestsFromName(test_file) for test_file in module_strings]
test_suite = unittest.TestSuite(suites)
test_runner = unittest.TextTestRunner().run(test_suite)

نعم، ربما يكون من الأسهل فقط استخدام الأنف من القيام بذلك، ولكن هذا بجانب النقطة.

إذا كنت ترغب في تشغيل جميع الاختبارات من فئات حالة الاختبار المختلفة، فأنت سعيد بتحديدها صراحة، يمكنك القيام بذلك مثل هذا:

from unittest import TestLoader, TextTestRunner, TestSuite
from uclid.test.test_symbols import TestSymbols
from uclid.test.test_patterns import TestPatterns

if __name__ == "__main__":

    loader = TestLoader()
    tests = [
        loader.loadTestsFromTestCase(test)
        for test in (TestSymbols, TestPatterns)
    ]
    suite = TestSuite(tests)

    runner = TextTestRunner(verbosity=2)
    runner.run(suite)

أين uclid هو مشروعي و TestSymbols و TestPatterns هي الفئات الفرعية TestCase.

لقد استخدمت discover طريقة وتحميل الزائد load_tests لتحقيق هذه النتيجة في خطوط أرقام (الحد الأدنى، على ما أعتقد)

def load_tests(loader, tests, pattern):
''' Discover and load all unit tests in all files named ``*_test.py`` in ``./src/``
'''
    suite = TestSuite()
    for all_test_suite in unittest.defaultTestLoader.discover('src', pattern='*_tests.py'):
        for test_suite in all_test_suite:
            suite.addTests(test_suite)
    return suite

if __name__ == '__main__':
    unittest.main()

تنفيذ على الخسيس شيء مثل

Ran 27 tests in 0.187s
OK

جربت العديد من الأساليب ولكن كل شيء يبدو معيبا أو لا بد لي من تصنيع بعض الكود، وهذا أمر مزعج. ولكن هناك طريقة مقدمة تحت Linux، وهذا ببساطة للعثور على كل اختبار من خلال نمط معين ثم استدعاء لهم واحدا تلو الآخر.

find . -name 'Test*py' -exec python '{}' \;

والأهم من ذلك، بالتأكيد يعمل.

في حالة معبئ مكتبة أو تطبيق، أنت لا تريد أن تفعل ذلك. setuptools سوف تفعل ذلك من أجلك.

لاستخدام هذا الأمر، يجب أن تكون اختبارات مشروعك في unittest جناح الاختبار إما عن طريق الوظيفة أو فئة TESTCASE أو الطريقة أو الوحدة أو حزمة تحتوي على TestCase الطبقات. إذا كان Suite المسمى هو وحدة نمطية، والوحدة لديها additional_tests() وظيفة، يسمى والنتيجة (التي يجب أن تكون unittest.TestSuite) يضاف إلى الاختبارات التي يجب تشغيلها. إذا كان الجناح المسمى عبارة عن حزمة، تتم إضافة أي نقل من الأسماك والتغذية بشكل متكرر إلى جناح الاختبار الشامل.

فقط أخبره حيث حزمة اختبار الجذر الخاص بك، مثل:

setup(
    # ...
    test_suite = 'somepkg.test'
)

و اهرب python setup.py test.

قد يكون الاكتشاف المستند إلى الملفات مشكلة في بيثون 3، إلا إذا قمت بتجنب الواردات النسبية في جناح الاختبار الخاص بك، لأن discover يستخدم استيراد الملفات. على الرغم من أنها تدعم اختياري top_level_dir, ، ولكن كان لدي بعض الأخطاء الإصابة لانهائية. لذلك حلا بسيطا للحصول على رمز غير معتاد هو وضع ما يلي في __init__.py من حزمة الاختبار الخاصة بك (انظر load_tests بروتوكول).

import unittest

from . import foo, bar


def load_tests(loader, tests, pattern):
    suite = unittest.TestSuite()
    suite.addTests(loader.loadTestsFromModule(foo))
    suite.addTests(loader.loadTestsFromModule(bar))

    return suite

يمكنني استخدام Pydev / Liclipse ولم أحصل حقا على كيفية تشغيل جميع الاختبارات في وقت واحد من واجهة المستخدم الرسومية. (تحرير: انقر بزر الماوس الأيمن فوق مجلد اختبار الجذر واختر Run as -> Python unit-test

هذا هو الحل الحالي الخاص بي:

import unittest

def load_tests(loader, tests, pattern):
    return loader.discover('.')

if __name__ == '__main__':
    unittest.main()

أضع هذا الرمز في وحدة تسمى all في دليل الاختبار الخاص بي. إذا قمت بتشغيل هذه الوحدة كأحد من Liclipse ثم يتم تشغيل جميع الاختبارات. إذا طلبت فقط تكرار الاختبارات المحددة أو الفاشلة، يتم تشغيل تلك الاختبارات فقط. لا تتداخل مع عداء اختبار Commentline الخاص بي إما (nosetests) - يتم تجاهله.

قد تحتاج إلى تغيير الحجج إلى discover بناء على إعداد المشروع الخاص بك.

بناء على إجابة ستيفن cagle. أضفت دعم وحدات الاختبار المتداخلة.

import fnmatch
import os
import unittest

def all_test_modules(root_dir, pattern):
    test_file_names = all_files_in(root_dir, pattern)
    return [path_to_module(str) for str in test_file_names]

def all_files_in(root_dir, pattern):
    matches = []

    for root, dirnames, filenames in os.walk(root_dir):
        for filename in fnmatch.filter(filenames, pattern):
            matches.append(os.path.join(root, filename))

    return matches

def path_to_module(py_file):
    return strip_leading_dots( \
        replace_slash_by_dot(  \
            strip_extension(py_file)))

def strip_extension(py_file):
    return py_file[0:len(py_file) - len('.py')]

def replace_slash_by_dot(str):
    return str.replace('\\', '.').replace('/', '.')

def strip_leading_dots(str):
    while str.startswith('.'):
       str = str[1:len(str)]
    return str

module_names = all_test_modules('.', '*Tests.py')
suites = [unittest.defaultTestLoader.loadTestsFromName(mname) for mname 
    in module_names]

testSuite = unittest.TestSuite(suites)
runner = unittest.TextTestRunner(verbosity=1)
runner.run(testSuite)

يبحث الرمز جميع الدلائل الفرعية . ل *Tests.py الملفات التي يتم تحميلها بعد ذلك. يتوقع كل منهما *Tests.py لاحتواء فئة واحدة *Tests(unittest.TestCase) التي يتم تحميلها بدورها وتنفذها واحدة تلو الأخرى.

يعمل هذا مع التعشيش العميق التعسفي من الدلائل / الوحدات النمطية، ولكن يحتاج كل دليل بين الاحتياجات فارغة __init__.py ملف على الأقل. يسمح هذا باختبار تحميل الوحدات المتداخلة عن طريق استبدال القطابق (أو الخلفية) بواسطة النقاط (انظر replace_slash_by_dot).

لأن اختبار اختبار الاختبار هو موضوع كامل، هناك بعض الإطار المخصص لاختبار الاكتشاف:

المزيد من القراءة هنا: https://wiki.python.org/moin/pythontestingtoolstaolstail.

سيقوم البرنامج النصي باش هذا بتنفيذ دليل اختبار Python UniTtest من أي مكان في نظام الملفات، بغض النظر عن دليل العمل الذي تقوم به: دليل العمل الخاص به دائما أين test يقع الدليل.

جميع الاختبارات، PWD مستقلة

Unittest Python وحدة حساسة للدليل الحالي الخاص بك، إلا إذا أخبرته أين (باستخدام discover -s اختيار).

هذا مفيد عند البقاء في ./src أو ./example دليل العمل وتحتاج إلى اختبار وحدة إجمالية سريعة:

#!/bin/bash
this_program="$0"
dirname="`dirname $this_program`"
readlink="`readlink -e $dirname`"

python -m unittest discover -s "$readlink"/test -v

اختبارات مختارة، PWD مستقلة

اسم ملف الأداة المساعدة هذا: runone.py واستخدامها مثل هذا:

runone.py <test-python-filename-minus-dot-py-fileextension>
#!/bin/bash
this_program="$0"
dirname="`dirname $this_program`"
readlink="`readlink -e $dirname`"

(cd "$dirname"/test; python -m unittest $1)

لا حاجة ل test/__init__.py ملف لعبء الحزمة / الذاكرة النفقات العامة أثناء الإنتاج.

هنا هو نهج بلدي عن طريق خلق غلاف لتشغيل الاختبارات من سطر الأوامر:

#!/usr/bin/env python3
import os, sys, unittest, argparse, inspect, logging

if __name__ == '__main__':
    # Parse arguments.
    parser = argparse.ArgumentParser(add_help=False)
    parser.add_argument("-?", "--help",     action="help",                        help="show this help message and exit" )
    parser.add_argument("-v", "--verbose",  action="store_true", dest="verbose",  help="increase output verbosity" )
    parser.add_argument("-d", "--debug",    action="store_true", dest="debug",    help="show debug messages" )
    parser.add_argument("-h", "--host",     action="store",      dest="host",     help="Destination host" )
    parser.add_argument("-b", "--browser",  action="store",      dest="browser",  help="Browser driver.", choices=["Firefox", "Chrome", "IE", "Opera", "PhantomJS"] )
    parser.add_argument("-r", "--reports-dir", action="store",   dest="dir",      help="Directory to save screenshots.", default="reports")
    parser.add_argument('files', nargs='*')
    args = parser.parse_args()

    # Load files from the arguments.
    for filename in args.files:
        exec(open(filename).read())

    # See: http://codereview.stackexchange.com/q/88655/15346
    def make_suite(tc_class):
        testloader = unittest.TestLoader()
        testnames = testloader.getTestCaseNames(tc_class)
        suite = unittest.TestSuite()
        for name in testnames:
            suite.addTest(tc_class(name, cargs=args))
        return suite

    # Add all tests.
    alltests = unittest.TestSuite()
    for name, obj in inspect.getmembers(sys.modules[__name__]):
        if inspect.isclass(obj) and name.startswith("FooTest"):
            alltests.addTest(make_suite(obj))

    # Set-up logger
    verbose = bool(os.environ.get('VERBOSE', args.verbose))
    debug   = bool(os.environ.get('DEBUG', args.debug))
    if verbose or debug:
        logging.basicConfig( stream=sys.stdout )
        root = logging.getLogger()
        root.setLevel(logging.INFO if verbose else logging.DEBUG)
        ch = logging.StreamHandler(sys.stdout)
        ch.setLevel(logging.INFO if verbose else logging.DEBUG)
        ch.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(name)s: %(message)s'))
        root.addHandler(ch)
    else:
        logging.basicConfig(stream=sys.stderr)

    # Run tests.
    result = unittest.TextTestRunner(verbosity=2).run(alltests)
    sys.exit(not result.wasSuccessful())

من أجل البساطة، يرجى عذرا غيربيب 8. معايير الترميز.

ثم يمكنك إنشاء فئة BaseTest للمكونات الشائعة لجميع الاختبارات الخاصة بك، لذلك سيظهر كل من اختبارك ببساطة:

from BaseTest import BaseTest
class FooTestPagesBasic(BaseTest):
    def test_foo(self):
        driver = self.driver
        driver.get(self.base_url + "/")

لتشغيل، يمكنك ببساطة تحديد الاختبارات كجزء من وسيطات سطر الأوامر، على سبيل المثال:

./run_tests.py -h http://example.com/ tests/**/*.py
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top