28

unittestいくつかの機能テストを行うためにPython2.7フレームワークを拡張しています。私がやりたいことの1つは、すべてのテストがテスト内およびsetUpClass()メソッド内で実行されないようにすることです。テストが失敗した場合、プログラムが壊れてテストを続けることができなくなることがあるので、テストの実行を停止したいと思います。

TestResultにshouldStop属性とメソッドがあることに気づきstop()ましたが、テスト内でそれにアクセスする方法がわかりません。

誰かアイデアはありますか?もっと良い方法はありますか?

4

8 に答える 8

27

興味がある場合は、 py.testを使用してテストスイートをクリーンに終了する方法を自分で決定する簡単な例を次に示します。

# content of test_module.py
import pytest
counter = 0
def setup_function(func):
    global counter
    counter += 1
    if counter >=3:
        pytest.exit("decided to stop the test run")

def test_one():
    pass
def test_two():
    pass
def test_three():
    pass

これを実行すると、次のようになります。

$ pytest test_module.py 
============== test session starts =================
platform linux2 -- Python 2.6.5 -- pytest-1.4.0a1
test path 1: test_module.py

test_module.py ..

!!!! Exit: decided to stop the test run !!!!!!!!!!!!
============= 2 passed in 0.08 seconds =============

py.test.exit()呼び出しをテスト内またはプロジェクト固有のプラグインに入れることもできます。

補足:NUMの失敗後の停止の実装をpy.testネイティブにサポートします。py.test --maxfail=NUM

Sidenote2:従来のスタイルpy.testでテストを実行するためのサポートは限られています。unittest.TestCase

于 2010-09-29T11:46:55.270 に答える
5

これが私がしばらくして思いついた別の答えです:

まず、新しい例外を追加しました。

class StopTests(Exception):
"""
Raise this exception in a test to stop the test run.

"""
    pass

assert次に、子テストクラスに新しいものを追加しました。

def assertStopTestsIfFalse(self, statement, reason=''):
    try:
        assert statement            
    except AssertionError:
        result.addFailure(self, sys.exc_info())

最後に、run関数をオーバーライドして、testMethod()呼び出しのすぐ下にこれを含めます。

except StopTests:
    result.addFailure(self, sys.exc_info())
    result.stop()

すべてのテストですべてのテストを停止できるようになり、cpython固有のコードがないため、これがより気に入っています。

于 2012-02-03T00:22:46.467 に答える
4

現在、テストはスイートレベルでのみ停止できます。に入ると、テストを繰り返すときにTestCasestop()メソッドは使用されません。TestResult

質問にいくらか関連していますが、Python 2.7を使用している-f/--failfast場合は、でテストを呼び出すときにフラグを使用できますpython -m unittest。これにより、最初の失敗でテストが停止します。

25.3.2.1を参照してください。failfast、catch、およびbufferコマンドラインオプション

Noseを使用してテストを実行し、-x, --stop フラグを使用してテストを早期に停止することも検討できます。

于 2010-09-27T19:58:21.303 に答える
2

のテストループでは、開始時に条件unittest.TestSuiteがあります。break

class TestSuite(BaseTestSuite):

    def run(self, result, debug=False):
        topLevel = False
        if getattr(result, '_testRunEntered', False) is False:
            result._testRunEntered = topLevel = True

        for test in self:
            if result.shouldStop:
                break

したがって、私は次のようなカスタムテストスイートを使用しています。

class CustomTestSuite(unittest.TestSuite):
    """ This variant registers the test result object with all ScriptedTests,
        so that a failed Loign test can abort the test suite by setting result.shouldStop
        to True
    """
    def run(self, result, debug=False):
        for test in self._tests:
            test.result = result

        return super(CustomTestSuite, self).run(result, debug)

次のようなカスタムテスト結果クラスを使用します。

class CustomTestResult(TextTestResult):
    def __init__(self, stream, descriptions, verbosity):
        super(CustomTestResult, self).__init__(stream, descriptions, verbosity)
        self.verbosity = verbosity
        self.shouldStop = False

私のテストクラスは次のようなものです。

class ScriptedTest(unittest.TestCase):
    def __init__(self, environment, test_cfg, module, test):
        super(ScriptedTest, self).__init__()
        self.result = None

特定の条件下で、テストスイートを中止します。たとえば、テストスイートはログインで始まり、それが失敗した場合は、残りを試す必要はありません。

    try:
        test_case.execute_script(test_command_list)
    except AssertionError as e:
        if test_case.module == 'session' and test_case.test == 'Login':
            test_case.result.shouldStop = True
            raise TestFatal('Login failed, aborting test.')
        else:
            raise sys.exc_info()

次に、次のようにテストスイートを使用します。

    suite = CustomTestSuite()

    self.add_tests(suite)

    result = unittest.TextTestRunner(verbosity=self.environment.verbosity, stream=UnitTestLoggerStream(self.logger),
                                     resultclass=CustomTestResult).run(suite)

それを行うためのより良い方法があるかどうかはわかりませんが、私のテストでは正しく動作します。

于 2014-06-10T13:01:36.677 に答える
2

これまでに実行されたテストの通常のテストレポートは取得できませんが、メソッド内からテスト実行を停止する非常に簡単なTestCase方法は、メソッド内で発生させることKeyboardInterruptです。

内のCPythonのコードを見ると、のテストランナー内でのみKeyboardInterruptバブルアップが許可されていることがわかります。unittesttestPartExecutor()

于 2017-08-05T04:55:13.147 に答える
2

OPはPython2.7に関するものでした。10年先にスキップし、python 3.1以降では、python unittestでテストをスキップする方法がアップグレードされましたが、ドキュメントではいくつかの説明(IMHO)を使用できます。

ドキュメントの内容は次のとおりです。

  • 最初の失敗後にすべてのテストをスキップする:使用しますfailfast(他の無関係なTestCaseクラスを含め、それ以上のテストをまったく続行したくない場合にのみ役立ちます)
  • TestCaseクラスのすべてのテストをスキップする:クラスを@unittest.skip()などで飾ります。
  • TestCase内の単一のメソッドをスキップします:メソッドを@unittest.skip()、などで装飾します。
  • @unittest.skipIf()条件付きでメソッドまたはクラスをスキップします:または@unittest.skipUnless()などで装飾します。
  • 条件付きでメソッドをスキップしますが、そのメソッド内で何かが実行されるまではスキップしません。メソッド内で使用しますself.skipTest()(これにより、そのメソッドはスキップされ、後続のメソッドではなく、そのメソッドのみがスキップされます)

ドキュメントは以下をカバーしていません(この記事の執筆時点で):

  1. setUpClassメソッド内で条件が満たされた場合は、TestCaseクラス内のすべてのテストをスキップします:この投稿からの解決策 raise unittest.SkipTest("skip all tests in this class")(別の方法があるかもしれませんが、私は知りません)
  2. 最初のテストの1つで条件が満たされた後、TestCaseクラスの後続のすべてのテストメソッドをスキップしますが、それでも他の無関係なTestCaseクラスのテストを続行します。このために、私は次の解決策を提案します...

このソリューションは、テストメソッドの途中で「不良状態」が発生し、テストメソッドでのみ認識できることを前提としています(つまり、何らかの理由でsetUpClassメソッドで判別できたものではありません)。 )。実際、setUpClassメソッドは、初期条件が正しくない場合に続行するかどうかを決定するのに最適な場所ですが、(私が遭遇したように)テストメソッドを実行するまでわからない場合があります。このソリューションは、テストメソッドがアルファベット順になっており、「不良」状態が発生した後に実行したくない後続のテストメソッドがアルファベット順に続くことを前提としています。

import unittest

class SkipMethodsConditionally(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        #this class variable maintains whether or not test methods should continue
        cls.should_continue = True
        #this class variable represents the state of your system. Replace with function of your own
        cls.some_bad_condition = False

    def setUp(self) -> None:
        """setUp runs before every single test method in this class"""
        if not self.__class__.should_continue:
            self.skipTest("no reason to go on.")

    def test_1_fail(self):
        #Do some work here. Let's assume you encounter a "bad state,"" that could 
        #only be noticed in this first test method only, (i.e., it's not something that
        #can be placed in the setUpClass method, for whatever reason)
        self.__class__.some_bad_condition = True

        if self.__class__.some_bad_condition:
            self.__class__.should_continue = False

        self.assertTrue(False,"this test should fail, rendering the rest of the tests irrelevant")

    def test_2_pass(self):
        self.assertFalse(self.__class__.some_bad_condition,"this test would pass normally if run, but should be skipped, because it would fail")

上記のテストでは、次の出力が得られます。

test_1_fail (__main__.SkipMethodsConditionally) ... FAIL
test_2_pass (__main__.SkipMethodsConditionally) ... skipped 'no reason to go on.'
----------------------------------------------------------------------
Ran 2 tests in 0.001s

FAILED (failures=1, skipped=1)
于 2020-11-19T22:22:00.733 に答える
0

私はクラスを見て、TestCaseそれをサブクラス化することにしました。クラスは単にオーバーライドしますrun()。メソッドをコピーし、元のクラスの318行目から次のように追加しました。

# this is CPython specific. Jython and IronPython may do this differently
if testMethod.func_code.co_argcount == 2:
    testMethod(result)
else:
    testMethod()

テストメソッドが別のパラメーターを受け入れることができるかどうかを示すCPython固有のコードがいくつかありますが、私はどこでもCPythonを使用しているので、これは私にとって問題ではありません。

于 2010-09-28T18:03:08.500 に答える
-3

使用する:

if condition: 
   return 'pass'
于 2018-10-23T22:30:07.180 に答える