รันการทดสอบเดี่ยวจาก unittest.TestCase ผ่านทางบรรทัดคำสั่ง


256

ในทีมของเราเรากำหนดกรณีทดสอบส่วนใหญ่ดังนี้:

คลาส "framework" หนึ่งคลาสourtcfw.py:

import unittest

class OurTcFw(unittest.TestCase):
    def setUp:
        # something

    # other stuff that we want to use everywhere

และกรณีทดสอบจำนวนมากเช่น testMyCase.py:

import localweather

class MyCase(OurTcFw):

    def testItIsSunny(self):
        self.assertTrue(localweather.sunny)

    def testItIsHot(self):
        self.assertTrue(localweather.temperature > 20)

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

เมื่อฉันเขียนรหัสทดสอบใหม่และต้องการเรียกใช้บ่อยๆและประหยัดเวลาสิ่งที่ฉันทำคือฉันใส่ "__" ไว้หน้าการทดสอบอื่น ๆ ทั้งหมด แต่มันเป็นเรื่องยุ่งยากกวนใจฉันจากรหัสที่ฉันเขียนและเสียงการกระทำที่สร้างขึ้นนั้นน่ารำคาญ

เช่นเมื่อมีการเปลี่ยนแปลงtestItIsHot()ฉันต้องการที่จะทำสิ่งนี้:

$ python testMyCase.py testItIsHot

และได้unittestทำงานเท่านั้น testItIsHot()

ฉันจะบรรลุสิ่งนั้นได้อย่างไร

ฉันพยายามที่จะเขียนซ้ำif __name__ == "__main__":ส่วน แต่เนื่องจากฉันใหม่เพื่อ Python ฉันรู้สึกหลงทางและทุบตีทุกอย่างนอกเหนือจากวิธีการ

คำตอบ:


311

สิ่งนี้ทำงานได้ตามที่คุณแนะนำ - คุณเพียงแค่ต้องระบุชื่อคลาสด้วย:

python testMyCase.py MyCase.testItIsHot

2
พุทโธ่! เนื่องจากการทดสอบจะทำงานบน python2.6 (99% ของเวลาที่ฉันสามารถทดสอบการทดสอบด้วย python2.7) ฉันจึงดูที่ 2.6.8 doc และพลาดไปมาก! :-)
Alois Mahdal

1
เพิ่งสังเกตเห็นว่าวิธีนี้ใช้ได้เฉพาะในกรณีที่วิธีการนั้นเรียกว่า "test *" ดังนั้นจึงไม่สามารถใช้เรียกใช้การทดสอบที่ "ปิดใช้งาน" เป็นครั้งคราวโดยเปลี่ยนชื่อ
Alois Mahdal

4
ไม่ทำงานสำหรับการทดสอบในไดเรกทอรีย่อย - กรณีที่พบบ่อยที่สุดในโปรแกรม Python สำหรับผู้ใหญ่
Tom Swirly

4
@TomSwirly ไม่สามารถตรวจสอบตอนนี้ แต่ฉันคิดว่าคุณสามารถทำได้โดยการสร้าง (ว่าง) __init__.pyภายใน direcrory (และตำบลถ้ามี) และโทรเช่น python test/testMyCase.py test.MyCase.testItIsHot.
Alois Mahdal

1
ไม่มีอะไรเกิดขึ้นเมื่อฉันทำสิ่งนี้ ฉันพบวิธีแก้ปัญหา แต่ฉันหวังว่าวิธีนี้จะได้ผลสำหรับฉัน
Joe Flack

152

หากคุณจัดระเบียบกรณีทดสอบของคุณนั่นคือให้ทำตามองค์กรเดียวกันเช่นรหัสจริงและใช้การอิมพอร์ตสัมพัทธ์สำหรับโมดูลในแพ็คเกจเดียวกัน

คุณยังสามารถใช้รูปแบบคำสั่งต่อไปนี้:

python -m unittest mypkg.tests.test_module.TestClass.test_method
# In your case, this would be:
python -m unittest testMyCase.MyCase.testItIsHot

เอกสาร Python3 สำหรับสิ่งนี้: https://docs.python.org/3/library/unittest.html#command-line-interface


นี่เป็น Java-esque "long_module_name.SameLongNameAsAClass.test_long_name_beginning_with_test_as_a_convention" ... หวังว่าคุณจะไม่ทำให้เป็นโมดูลในห้องสวีทเหมือนคนมีสติที่ทดสอบรหัสของพวกเขา
Joshua Detwiler

69

มันสามารถทำงานได้ดีตามที่คุณคาดเดา

python testMyCase.py MyCase.testItIsHot

และมีอีกวิธีในการทดสอบtestItIsHot:

    suite = unittest.TestSuite()
    suite.addTest(MyCase("testItIsHot"))
    runner = unittest.TextTestRunner()
    runner.run(suite)

11
ฉันพบว่าส่วนที่สองของคำตอบนี้มีประโยชน์อย่างยิ่ง: ฉันกำลังเขียนการทดสอบใน Eclipse + PyDev และฉันไม่ต้องการเปลี่ยนเป็นบรรทัดคำสั่ง!
Giovanni Di Milia

25

หากคุณตรวจสอบความช่วยเหลือของโมดูล unittest มันจะบอกคุณเกี่ยวกับการรวมกันหลายอย่างที่ช่วยให้คุณเรียกใช้คลาสกรณีทดสอบจากโมดูลและวิธีการทดสอบจากคลาสกรณีทดสอบ

python3 -m unittest -h

[...]

Examples:
  python3 -m unittest test_module               - run tests from test_module
  python3 -m unittest module.TestClass          - run tests from module.TestClass
  python3 -m unittest module.Class.test_method  - run specified test method

ไม่ต้องการให้คุณกำหนด a unittest.main()เป็นพฤติกรรมเริ่มต้นของโมดูลของคุณ


2
1 และตั้งแต่คำศัพท์ที่สามารถทำให้เกิดความสับสนถ้าใหม่เพื่อภาษา (และusageแม้จะไม่สอดคล้องกันอย่างผิดปกติ): การทำงาน python -m unittest module_test.TestClass.test_methodถือว่าเป็นไฟล์module_test.py(เรียกใช้จากไดเรกทอรีปัจจุบันและ__init.py__จะไม่จำเป็น); และmodule_test.pyประกอบด้วยclass TestClass(unittest.TestCase)...ซึ่งประกอบด้วยdef test_method(self,...)(สิ่งนี้ใช้ได้กับฉันใน python 2.7.13)
michael

10

อาจจะเป็นประโยชน์สำหรับใครบางคน ในกรณีที่คุณต้องการเรียกใช้การทดสอบจากคลาสเฉพาะ:

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

มันใช้งานได้สำหรับฉันใน python 3.6


3

แรงบันดาลใจจาก@yarkeeฉันรวมมันกับรหัสบางส่วนที่ฉันได้รับแล้ว นอกจากนี้คุณยังสามารถเรียกนี้จากสคริปต์อื่นเพียงโดยการเรียกฟังก์ชั่นโดยไม่ต้องใช้บรรทัดคำสั่งหรือเพียงแค่เรียกมันว่าจากบรรทัดคำสั่งด้วยrun_unit_tests()python3 my_test_file.py

import my_test_file
my_test_file.run_unit_tests()

น่าเศร้าที่ใช้งานได้เฉพาะกับPython 3.3หรือเหนือกว่า:

import unittest

class LineBalancingUnitTests(unittest.TestCase):

    @classmethod
    def setUp(self):
        self.maxDiff = None

    def test_it_is_sunny(self):
        self.assertTrue("a" == "a")

    def test_it_is_hot(self):
        self.assertTrue("a" != "b")

รหัสวิ่ง:

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import unittest
from .somewhere import LineBalancingUnitTests

def create_suite(classes, unit_tests_to_run):
    suite = unittest.TestSuite()
    unit_tests_to_run_count = len( unit_tests_to_run )

    for _class in classes:
        _object = _class()
        for function_name in dir( _object ):
            if function_name.lower().startswith( "test" ):
                if unit_tests_to_run_count > 0 \
                        and function_name not in unit_tests_to_run:
                    continue
                suite.addTest( _class( function_name ) )
    return suite

def run_unit_tests():
    runner = unittest.TextTestRunner()
    classes =  [
        LineBalancingUnitTests,
    ]

    # Comment all the tests names on this list, to run all Unit Tests
    unit_tests_to_run =  [
        "test_it_is_sunny",
        # "test_it_is_hot",
    ]
    runner.run( create_suite( classes, unit_tests_to_run ) )

if __name__ == "__main__":
    print( "\n\n" )
    run_unit_tests()

การแก้ไขโค้ดเล็กน้อยคุณสามารถส่งผ่านอาร์เรย์ด้วยการทดสอบหน่วยทั้งหมดที่คุณต้องการโทรหา:

...
def run_unit_tests(unit_tests_to_run):
    runner = unittest.TextTestRunner()

    classes = \
    [
        LineBalancingUnitTests,
    ]

    runner.run( suite( classes, unit_tests_to_run ) )
...

และไฟล์อื่น:

import my_test_file

# Comment all the tests names on this list, to run all Unit Tests
unit_tests_to_run = \
[
    "test_it_is_sunny",
    # "test_it_is_hot",
]

my_test_file.run_unit_tests( unit_tests_to_run )

หรือคุณสามารถใช้https://docs.python.org/3/library/unittest.html#load-tests-protocolและกำหนดวิธีการต่อไปนี้ในโมดูล / ไฟล์ทดสอบของคุณ:

def load_tests(loader, standard_tests, pattern):
    suite = unittest.TestSuite()

    # To add a single test from this file
    suite.addTest( LineBalancingUnitTests( 'test_it_is_sunny' ) )

    # To add a single test class from this file
    suite.addTests( unittest.TestLoader().loadTestsFromTestCase( LineBalancingUnitTests ) )

    return suite

หากคุณต้องการ จำกัด การประมวลผลให้เป็นไฟล์ทดสอบหนึ่งไฟล์คุณเพียงแค่ต้องตั้งค่ารูปแบบการค้นหาทดสอบเป็นไฟล์เดียวที่คุณกำหนดload_tests()ฟังก์ชันไว้

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import unittest

test_pattern = 'mytest/module/name.py'
PACKAGE_ROOT_DIRECTORY = os.path.dirname( os.path.realpath( __file__ ) )

loader = unittest.TestLoader()
start_dir = os.path.join( PACKAGE_ROOT_DIRECTORY, 'testing' )

suite = loader.discover( start_dir, test_pattern )
runner = unittest.TextTestRunner( verbosity=2 )
results = runner.run( suite )

print( "results: %s" % results )
print( "results.wasSuccessful: %s" % results.wasSuccessful() )

sys.exit( not results.wasSuccessful() )

อ้างอิง:

  1. ปัญหากับ sys.argv [1] เมื่อโมดูล unittest อยู่ในสคริปต์
  2. มีวิธีการวนซ้ำและดำเนินการฟังก์ชันทั้งหมดในคลาส Python หรือไม่
  3. วนลูปมากกว่าตัวแปรสมาชิกทั้งหมดของคลาสในหลาม

อีกทางเลือกหนึ่งตัวอย่างของโปรแกรมหลักสุดท้ายฉันพบกับรูปแบบต่อไปนี้หลังจากอ่านunittest.main()การใช้วิธีการ:

  1. https://github.com/python/cpython/blob/master/Lib/unittest/main.py#L65
#! /usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import sys
import unittest

PACKAGE_ROOT_DIRECTORY = os.path.dirname( os.path.realpath( __file__ ) )
start_dir = os.path.join( PACKAGE_ROOT_DIRECTORY, 'testing' )

from testing_package import main_unit_tests_module
testNames = ["TestCaseClassName.test_nameHelloWorld"]

loader = unittest.TestLoader()
suite = loader.loadTestsFromNames( testNames, main_unit_tests_module )

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

print( "results: %s" % results )
print( "results.wasSuccessful: %s" % results.wasSuccessful() )
sys.exit( not results.wasSuccessful() )

3

TL; DR : สิ่งนี้น่าจะใช้ได้:

python mypkg/tests/test_module.py MyCase.testItIsHot

คำอธิบาย :

  • วิธีที่สะดวก

    python mypkg/tests/test_module.py MyCase.testItIsHot

    จะใช้งานได้ แต่ข้อสันนิษฐานที่ไม่ได้พูดคือคุณมีข้อมูลโค้ดธรรมดานี้อยู่แล้ว (โดยปกติแล้วในตอนท้าย) ไฟล์ทดสอบของคุณ

    if __name__ == "__main__":
        unittest.main()
  • วิธีที่ไม่สะดวก

    python -m unittest mypkg.tests.test_module.TestClass.test_method

    จะทำงานได้เสมอโดยไม่ต้องให้คุณมีif __name__ == "__main__": unittest.main()ข้อมูลโค้ดนั้นในไฟล์ทดสอบของคุณ

ดังนั้นทำไมวิธีที่ 2 จึงถือว่าไม่สะดวก เนื่องจากอาจเป็นความเจ็บปวดใน (_ แทรกส่วนใดส่วนหนึ่งของร่างกายที่นี่ _) เพื่อพิมพ์เส้นทางยาวที่มีจุดคั่นด้วยมือ ในขณะที่ในวิธีที่ 1 ชิ้นmypkg/tests/test_module.pyส่วนสามารถทำให้สมบูรณ์อัตโนมัติได้ไม่ว่าจะเป็นเปลือกที่ทันสมัยหรือโดยเครื่องมือแก้ไขของคุณ

ป.ล. : ถ้าคุณคิดว่าส่วนของร่างกายอยู่ใต้เอวคุณก็เป็นคนที่แท้จริง :-) ฉันหมายถึงการพูดว่า "ข้อต่อนิ้ว" การพิมพ์มากเกินไปจะไม่ดีสำหรับข้อต่อของคุณ ;-)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.