Frage

Dies ist ein Follow-up zu einem vorherige Frage von mir.

In der vorherige Frage wurden Methoden untersucht, um das umzusetzen, was im Wesentlichen der gleiche Test über eine ganze Familie von Funktionen, Prüfung gewährleistet nicht beim ersten Stopp-Funktion hat, die fehlgeschlagen.

Meine bevorzugte Lösung verwendet, um eine Metaklasse dynamisch die Tests in eine unittest.TestCase einzufügen. Leider statisch nicht diese Nase holen, weil Nase für Testfälle durchsucht.

Wie erhalte ich die Nase zu entdecken und so einen Testcase laufen? Siehe hier für ein Beispiel für den Testfall in Frage.

War es hilfreich?

Lösung

Nase hat eine „Testgenerator“ -Funktion für Sachen wie diese. Sie schreiben eine Generatorfunktion, die jede „Testfall“ Funktion liefert Sie es, zusammen mit seiner args ausgeführt werden sollen. Nach Ihrer vorherigen Beispiel das jede der Funktionen in einem separaten Test überprüfen können:

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)

Andere Tipps

Nose sucht nicht nach Tests statisch, so dass Sie können verwenden metaclass Magie Tests zu machen, dass Nase findet.

Der schwierige Teil ist, dass Standard-Metaklasse Techniken nicht korrekt angeben, die func_name Attribut, das ist, was Nase sucht, wenn geprüft wird, ob Methoden auf Ihrer Klasse sind Tests.

Hier ist ein einfaches metaclass. Es sieht durch die func dict und fügt eine neue Methode für jede Methode es findet, zu behaupten, dass es das Verfahren fand eine docstring hat. Diese neuen Synthesemethoden sind die Namen "test_%d" %i gegeben.

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)

Lassen Sie uns nun eine neue Klasse erstellen, die diese Metaklasse verwendet

class Foo(object):
    __metaclass__ = Meta

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

    def greeter_no_docstring(self):
        pass

Zur Laufzeit Foo tatsächlich Test_Foo benannt werden und wird greeter, greeter_no_docstring, test_1 und test_2 als seine Methoden. Wenn ich nosetests auf diese Datei ausgeführt, hier ist die Ausgabe:

$ 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)

Dieses Metaklasse ist nicht wirklich nützlich wie es ist, aber wenn man stattdessen die Meta nicht als richtiger metaclass verwenden, sondern eher als eine funktionellen metaclass (dh nimmt eine Klasse als Argument und gibt eine neue Klasse, eine, die umbenannt ist so wird die Nase er) finden, dann ist es ist nützlich. Ich habe diesen Ansatz verwendet, um automatisch zu prüfen, dass die Docstrings zum Numpy Standard als Teil einer Nase Testsuite zu halten.

Außerdem habe ich eine Menge Ärger hatte ordnungsgemäße Schließung immer mit new.function arbeiten, weshalb dieser Code verwendet m(self, func) wo func gemacht wird ein Standardargument zu sein. Es wäre natürlich einen Verschluss über value zu verwenden, aber das scheint nicht zu funktionieren.

Sie könnten versuchen, die Testcase-Klassen mit Typ ()

zu erzeugen,
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 .."}
)

Dies sollte erstellt werden, wenn die Nase versucht Ihr test_module zu importieren, damit es funktionieren soll.

Der Vorteil dieses Ansatzes ist, dass Sie dynamisch viele Kombinationen von Tests erstellen können.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top