動的に生成されたテストケースを発見するにはどうすればいいですか?

StackOverflow https://stackoverflow.com/questions/347574

  •  19-08-2019
  •  | 
  •  

質問

これは、私の前の質問

前の質問では、機能のファミリ全体で本質的に同じテストであったものを実装する方法が検討され、失敗した最初の機能でテストが停止しないようにしました。

私が推奨するソリューションでは、メタクラスを使用してテストをunittest.TestCaseに動的に挿入しました。残念ながら、鼻はテストケースを静的にスキャンするため、鼻はこれを検出しません。

このようなTestCaseを発見して実行するには、どうすればいいですか? こちら問題のTestCaseの例。

役に立ちましたか?

解決

Noseには<!> quot; test generator <!> quot;があります。このようなもののための機能。各<!> quot;テストケース<!> quotを生成するジェネレーター関数を作成します。実行する関数とその引数。前の例に続いて、個別のテストで各関数を確認できます。

import unittest
import numpy

from somewhere import the_functions

def test_matrix_functions():
    for function in the_functions:
        yield check_matrix_function, function

def check_matrix_function(function)
    matrix1 = numpy.ones((5,10))
    matrix2 = numpy.identity(5)
    output = function(matrix1, matrix2)
    assert matrix1.shape == output.shape, \
           "%s produces output of the wrong shape" % str(function)

他のヒント

Noseはテストを静的にスキャンしないため、メタクラスマジックを使用して、Noseが検出するテストを作成できます。

難しいのは、標準のメタクラス手法ではfunc_name属性が正しく設定されないことです。これは、クラスのメソッドがテストであるかどうかをチェックするときにNoseが探すものです。

これは単純なメタクラスです。 func dictを調べて、見つかったすべてのメソッドに新しいメソッドを追加し、見つかったメソッドにdocstringがあることをアサートします。これらの新しい合成メソッドには、"test_%d" %i

という名前が付けられます。
import new
from inspect import isfunction, getdoc

class Meta(type):
    def __new__(cls, name, bases, dct):

        newdct = dct.copy()
        for i, (k, v) in enumerate(filter(lambda e: isfunction(e[1]), dct.items())):
            def m(self, func):
                assert getdoc(func) is not None

            fname = 'test_%d' % i
            newdct[fname] = new.function(m.func_code, globals(), fname,
                (v,), m.func_closure)

        return super(Meta, cls).__new__(cls, 'Test_'+name, bases, newdct)

今、このメタクラスを使用する新しいクラスを作成しましょう

class Foo(object):
    __metaclass__ = Meta

    def greeter(self):
        "sdf"
        print 'Hello World'

    def greeter_no_docstring(self):
        pass

実行時、Fooは実際にはTest_Fooという名前になり、メソッドとしてgreetergreeter_no_docstringtest_1、およびtest_2が付けられます。このファイルでnosetestsを実行すると、出力は次のようになります。

$ nosetests -v test.py
test.Test_Foo.test_0 ... FAIL
test.Test_Foo.test_1 ... ok

======================================================================
FAIL: test.Test_Foo.test_0
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Library/Frameworks/EPD64.framework/Versions/7.3/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
    self.test(*self.arg)
  File "/Users/rmcgibbo/Desktop/test.py", line 10, in m
    assert getdoc(func) is not None
AssertionError

----------------------------------------------------------------------
Ran 2 tests in 0.002s

FAILED (failures=1)

このメタクラスはそのままではあまり役に立ちませんが、代わりにMetaを適切なメタクラスとしてではなく、より機能的なメタクラスとして使用する場合(つまり、クラスを引数として、新しいクラスを返します)鼻がそれを見つけるように名前が変更されています)、それは便利です 。このアプローチを使用して、docstringsがノーズテストスイートの一部としてNumpy標準に準拠していることを自動的にテストしました。

また、new.functionで適切なクロージャを動作させるのに苦労しました。そのため、このコードではm(self, func)を使用します。ここで、funcはデフォルトの引数になります。 valueでクロージャを使用する方が自然ですが、それはうまくいかないようです。

type()を使用してテストケースクラスを生成しようとすることができます

class UnderTest_MixIn(object):

    def f1(self, i):
        return i + 1

    def f2(self, i):
        return i + 2

SomeDynamicTestcase = type(
    "SomeDynamicTestcase", 
    (UnderTest_MixIn, unittest.TestCase), 
    {"even_more_dynamic":"attributes .."}
)

# or even:

name = 'SomeDynamicTestcase'
globals()[name] = type(
    name, 
    (UnderTest_MixIn, unittest.TestCase), 
    {"even_more_dynamic":"attributes .."}
)

これは、noseがtest_moduleをインポートしようとするときに作成されるため、動作するはずです。

このアプローチの利点は、テストの多くの組み合わせを動的に作成できることです。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top