2010-12-30 14 views
33

いくつかのtest_ *メソッドを持つ抽象的なTestCaseを作成することはできますが、このTestCaseは呼び出されず、それらのメソッドはサブクラスでのみ使用されますか?私は私のテストスイートに1つの抽象的なTestCaseを持つつもりだと思うし、それは1つのインタフェースのいくつかの異なる実装のためにサブクラス化されます。このため、すべてのテストメソッドは、唯一の内部メソッドの変更の一部です。どのように私はエレガントな方法でそれを行うことができますか?python unittestを使用した抽象テストケース

+2

これは、テストを実行するために鼻を使用するとやや簡単かもしれません。 [テストの検索と実行](http://somethingaboutorange.com/mrl/projects/nose/1.0.0/finding_tests.html)を参照してください。鼻では、たとえば、ベースクラスに '__test __ = False'を入れることができます。 – bstpierre

+0

抽象的なテストケースでスキップを使用することはどうですか? – gruszczy

+0

@bstpierre素晴らしい提案。それは私のために完璧に働いた –

答えて

52

私は非常にあなたがする予定ですか何を理解していなかった - 親指のルールは、「テストとスマートではない」である - 単なる書かれ、そこにそれらを持っています。

しかし、あなたがunittest.main()を呼び出すたびにunittest.TestCaseを継承すると、抽象クラスが実行されます。これは避けたい状況です。

TestCaseではなく、「オブジェクト」を継承する「抽象的な」クラスを作成します。 実際の "具体的な"実装では、unittest.TestCaseと抽象クラスの両方から継承した複数の継承を使用してください。

import unittest 

class Abstract(object): 
    def test_a(self): 
     print "Running for class", self.__class__ 

class Test(Abstract, unittest.TestCase): 
    pass 

unittest.main() 

更新は: - そのdefintionsが同様に蛇腹コメント欄で指摘し、TestCaseデフォルトで上書きされないように第一Abstract継承順序を逆転させました。

+2

これは私が最初にこれをやりたかった方法です。私は別のデータベースに対して自分のテストを実行しているので、賢くなりたいです。データベースにアクセスするためのインターフェイスは常に同じなので、データベースへの接続をインスタンス化するだけで、常に同じテストが実行されます。私はそれがテストにはあまりにもスマートなのか分かりませんが、たくさん入力するのが好きではありません;-) – gruszczy

+0

はい、私は自分自身を「スマートなものにする」のが好きです。だから...私の答えに欠けているものを明確にすることができますか?コマンドラインのパラメータに応じて、testCaseクラスのテストメソッドを "プラグ"する方法があります。 – jsbueno

+0

私が仕事に就いていないとき、私は非常に賢いことをするのが好きです。このようにして、何かをねじ込んでも、もっと学ぶことができます:-)ここにはリスクはありません。 – gruszczy

3

run_unittestのすべてのテストクラスを明示的にリストするという慣例に従うと、にはまっすぐになり、は特定のクラスをリストします。

unittest.mainの使用を継続したい場合、unittest2の使用を許可する場合(例えばPython 2.7から)は、load_testsプロトコルを使用して、テストケースを含むクラスを指定できます。以前のバージョンでは、TestLoaderをサブクラス化し、loadTestsFromModuleをオーバーライドする必要があります。

+0

コマンドラインからこれらのテストを呼び出す能力を失うことはありませんか?デフォルトでは、すべてのテスト(抽象的なテストケースのものを除く)を実行したいと思いますが、場合によってはそれらのうちのいくつかのみを実行します。 – gruszczy

+0

@ gruszczy:私の編集を参照してください。 –

9

多重継承は、主に次の二つの理由から、ここでは偉大な選択肢ではないではありません。TestCase使用super()のメソッドのいずれ

  1. ので、あなたはsetUp()のような方法のために最初にあなたのクラスを一覧表示する必要があると思いますし、 tearDown()が動作します。
  2. pylintは、その時点でselfで定義されていないself.assertEquals()などをベースクラスが使用していることを警告します。

私が思いついたクルーは、run()を基本クラスのノーオペレーションに限定してください。

class TestBase(unittest.TestCase): 

    def __init__(self, *args, **kwargs): 
    super(TestBase, self).__init__(*args, **kwargs) 
    self.helper = None 
    # Kludge alert: We want this class to carry test cases without being run 
    # by the unit test framework, so the `run' method is overridden to do 
    # nothing. But in order for sub-classes to be able to do something when 
    # run is invoked, the constructor will rebind `run' from TestCase. 
    if self.__class__ != TestBase: 
     # Rebind `run' from the parent class. 
     self.run = unittest.TestCase.run.__get__(self, self.__class__)       
    else: 
     self.run = lambda self, *args, **kwargs: None 

    def newHelper(self): 
    raise NotImplementedError() 

    def setUp(self): 
    print "shared for all subclasses" 
    self.helper = self.newHelper() 

    def testFoo(self): 
    print "shared for all subclasses" 
    # test something with self.helper 

class Test1(TestBase): 
    def newHelper(self): 
    return HelperObject1() 

class Test2(TestBase): 
    def newHelper(self): 
    return HelperObject2() 
+3

(1)は本当に問題ではありません - あなたのクラスが最初に来てスーパーを使用することです(私の答えは逆でした - 。 (2)あなたが使用している言葉で言えば、サードパーティ製の糸くず取りツールが正しくイントロスペクトすることができないため、PythonのOOPに組み込まれたきれいなメカニズムを使用しないように "kludge"を使用する必要があります。それはあなたのツールの選択です。 – jsbueno

7

それはおそらく、いくつかの規則に反するが、ちょうど私の2セント-に入れて、あなたはその実行を防止するための保護されたメンバーとしてあなたの抽象テストケースを定義することができます。私はDjangoで以下を実装し、必要に応じて動作します。以下の例を参照してください。

from django.test import TestCase 


class _AbstractTestCase(TestCase): 

    """ 
    Abstract test case - should not be instantiated by the test runner. 
    """ 

    def test_1(self): 
     raise NotImplementedError() 

    def test_2(self): 
     raise NotImplementedError() 


class TestCase1(_AbstractTestCase): 

    """ 
    This test case will pass and fail. 
    """ 

    def test_1(self): 
     self.assertEqual(1 + 1, 2) 


class TestCase2(_AbstractTestCase): 

    """ 
    This test case will pass successfully. 
    """ 

    def test_1(self): 
     self.assertEqual(2 + 2, 4) 

    def test_2(self): 
     self.assertEqual(12 * 12, 144) 
+2

これはうまくいくようで、上のサルパッチアプローチよりもずっと簡単です。 – Fasaxc

+1

これはdjango(と私の場合は鼻で動作するかもしれませんが、unittest発見メカニズムはまだこれらの抽象的なテストケースを見つけるようです... –

+1

djangoでは動作しません。ちょうどテストした –

2

Pythonのunittestのライブラリには、あなたが正確に何をしたいです達成するために使用することができload_tests protocolを、持っている:

# Add this function to module with AbstractTestCase class 
def load_tests(loader, tests, _): 
    result = [] 
    for test_case in tests: 
     if type(test_case._tests[0]) is AbstractTestCase: 
      continue 
     result.append(test_case) 
    return loader.suiteClass(result) 
2

誰もがこれまで見逃したこと非常に簡単な方法があります。いくつかの回答とは異なり、すべてテストドライバで動作します。

del AbstractTestCase 

をモジュールの終わりに:

だけで、その後追加し、いつものように継承を使用しています。

+0

欠点はありますか? –

+0

@MaxMalyshスーパークラスへの古いスタイルの呼び出しは使用できません。しかし、「スーパー」はまだ動作しており、誰もが本当にそれを使用すべきです。 – o11c

関連する問題