[Python] Wie bekomme ich die Nase, um dynamisch generierte Testfälle zu entdecken?


Answers

Die Nase scannt nicht statisch nach Tests, also kannst du Metaklassen-Magie benutzen, um Tests zu machen, die Nase findet.

Der schwierige Teil ist, dass Standardmetaklasse-Techniken das Attribut func_name nicht richtig setzen, worauf Nase achtet, wenn sie prüft, ob Methoden in Ihrer Klasse Tests sind.

Hier ist eine einfache Metaklasse. Es sieht durch das func dict und fügt für jede gefundene Methode eine neue Methode hinzu, die besagt, dass die gefundene Methode einen Docstring hat. Diese neuen synthetischen Methoden erhalten die Namen "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)

Erstellen wir nun eine neue Klasse, die diese Metaklasse verwendet

class Foo(object):
    __metaclass__ = Meta

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

    def greeter_no_docstring(self):
        pass

Zur Laufzeit wird Foo tatsächlich Test_Foo und greeter , greeter_no_docstring , test_1 und test_2 als Methoden haben. Wenn ich nosetests für diese Datei verwende, ist hier 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)

Diese Metaklasse ist nicht wirklich nützlich wie sie ist, aber wenn Sie stattdessen die Meta nicht als eine richtige Metaklasse verwenden, sondern als eine funktionale Metaklasse (dh nimmt eine Klasse als Argument und gibt eine neue Klasse zurück, die umbenannt wird, so dass Nase wird es finden), dann ist es nützlich. Ich habe diesen Ansatz verwendet, um automatisch zu testen, ob die Docstrings als Teil einer Nasentestsuite dem Numpy-Standard entsprechen.

Außerdem hatte ich eine Menge Probleme damit, eine ordnungsgemäße Schließung mit new.function zu erreichen, weshalb dieser Code m(self, func) wobei func als Standardargument verwendet wird. Es wäre natürlicher, eine Schließung über den value , aber das scheint nicht zu funktionieren.

Question

Dies ist eine Fortsetzung meiner vorherigen Frage .

In der vorhergehenden Frage wurden Methoden untersucht, um im Wesentlichen den gleichen Test über eine ganze Familie von Funktionen zu implementieren, um sicherzustellen, dass das Testen nicht bei der ersten fehlgeschlagenen Funktion aufhört.

Meine bevorzugte Lösung verwendete eine Metaklasse, um die Tests dynamisch in einen Unittest einzufügen. TestCase. Leider nimmt die Nase das nicht auf, weil die Nase statisch nach Testfällen sucht.

Wie bekomme ich die Nase, um ein solches TestCase zu entdecken und zu betreiben? Bitte beziehen Sie sich hier auf ein Beispiel des betreffenden Testfalls.