Seite 1 von 1

Eine Funktion als Parameter übergeben mit beliebigen vielen Parametern

Verfasst: Sonntag 3. November 2019, 21:26
von losgehts
Hallo allerseits,

ich schreibe gerade einige Tests (Python3.xx) für eine Klasse und möchte gerne auch Assertions und Exceptions testen.

Dafür würde ich gerne eine Funktion benutzen, die als Argumente den erwarteten Fehler, die zu verwendende Funktion und ihre Parameter hat.
Beispiel: meine Funktionen func(a) und func_b(a, b) möchte ich mit der Funktion test_error() testen, sie erwarten aber eine unterschiedliche Anzahl an Parametern:

Code: Alles auswählen

def test_error(error, function, args):
    try:
        function(args)
        return False
    except error:
        return True
    except:
        print('anderer Fehler')
        return False    

def func(a):
    assert (a > 0), 'a has to be positive!'
    return True

def func_b(a, b):
    assert (a > b), 'a has to be greater than b!'
    return True


if __name__ == '__main__':
    test = test_error(AssertionError, func, -1)         # funktioniert !
    print(test)

    test = test_error(AssertionError, func_b, [1, 2])     # gesuchte Erweiterung
    print(test)
Will ich Funktionen mit nur einem Argument (so wie Funktion func) testen, so klappt das auch.
Doch wenn ich eine Funktion mit mehreren Argumenten testen möchte, geht das natürlich so nicht.

Jetzt könnte ich natürlich für jede Anzahl an Argumenten eine eigene Funktion test_error schreiben, doch ist das nötig? Gibt es die Möglichkeit der Funktion test_error eine Liste (oder ähnliches) der Argumente zu übergeben und diese beim eigentlichen Funktionsaufruf in einzelne Argumente "umzuwandeln"?

Auch wenn ich hier schon ewig angemeldet bin, so habe ich Python immer nur rudimentär eingesetzt und übergebe gerade das erste Mal eine Funktion als Parameter ...

Im Voraus vielen Dank!

Viele Grüße,
Ulrich

Re: Eine Funktion als Parameter übergeben mit beliebigen vielen Parametern

Verfasst: Sonntag 3. November 2019, 22:05
von Sirius3
Statt selbst etwas für Unit-Tests zu schreiben, solltest Du Dir ein etabliertes Framework wie ›pytest‹ anschauen.

Re: Eine Funktion als Parameter übergeben mit beliebigen vielen Parametern

Verfasst: Montag 4. November 2019, 01:09
von __blackjack__
@losgehts: Bei den ``assert`` sind die Klammern um die Bedingungen überflüssig. Die Messages sind IMHO recht redundant. Was die aussagen sieht man doch ganz wunderbar an der Bedingung, und die bekommt man ja auch zu sehen wenn die Zusicherung nicht stimmt. Wobei man auch aufpassen muss ``assert`` nicht als Ersatz für normale Ausnahmen zu verwenden. Mit ``assert`` testet man nur Sachen die wirklich nie schief gehen können.

Was Du suchst ist das ``*``. Schau mal im Tutorial in der Python-Dokumentation nach was das so zu Funktionsaufrufen sagt. Schlüsselwortargumente willst Du bei der Gelegenheit dann vielleicht auch gleich durchreichen.

Die Ausgabe ”ein anderer Fehler” ohne dem Benutzer die Möglichkeit zu geben zu sehen *welcher* denn und *wo* ist zur Fehlersuche ein Albtraum.

Aber grundsätzlich schliesse ich mich Sirius3 an — nichts selber erfinden solange es da nicht schon verbreitete Werkzeuge für gibt. Selbst das furchtbare `unittest` aus der Standardbibliothek hat `assertRaises()` & Co, aber `pytest` ist einfach ”pythonischer”.

Code: Alles auswählen

#!/usr/bin/env python3
import pytest


def func(a):
    assert a > 0, "a has to be positive!"
    return True


def func_b(a, b):
    assert a > b, "a has to be greater than b!"
    return True


def test_func():
    with pytest.raises(AssertionError):
        func(-1)


def test_func_b():
    with pytest.raises(AssertionError):
        func_b(1, 2)
Testlauf:

Code: Alles auswählen

$ pytest forum12.py
============================= test session starts ==============================
platform linux -- Python 3.6.8, pytest-3.8.0, py-1.6.0, pluggy-0.7.1
rootdir: /home/bj, inifile:
plugins: cov-2.7.1
collected 2 items                                                              

forum12.py ..                                                            [100%]

=========================== 2 passed in 0.01 seconds ===========================

Re: Eine Funktion als Parameter übergeben mit beliebigen vielen Parametern

Verfasst: Montag 4. November 2019, 23:53
von losgehts
Hallo Sirius3, hallo _blackjack_,

vielen Dank für eure Antworten!

OK, ihr habt mich überzeugt, ich werde pytest verwenden. Die ersten Versuche damit haben schon mal geklappt. Und jetzt muss ich mich halt ein wenig einarbeiten.

@_blackjack_
Dir danke ich ganz herzlich, dass du mir zusätzlich so konkret Feedback zu dem code gegeben hast. Damit hatte ich überhaupt nicht gerechnet. Das ist auch nicht mein original-code, sondern ein extra für die Fragestellung geschriebener. Ich werde mir merken, dass hier so konstruktiv Hilfe gegeben wird, dass ich das nächste Mal meine Frage näher am Originalcode stellen werde, sodass ich solche konkreten Tipps auch direkt verwenden kann.

Vielen Dank nochmals!

Grüße, Ulrich

Re: Eine Funktion als Parameter übergeben mit beliebigen vielen Parametern

Verfasst: Dienstag 5. November 2019, 00:30
von __blackjack__
Die beiden Testfunktionen sehen ja fast gleich aus, das könnte man mit `pytest` auch in *einer* parametrisierten Testfunktion machen:

Code: Alles auswählen

@pytest.mark.parametrize(
    "function, arguments", [(func, [-1]), (func_b, [1, 2])]
)
def test_functions_for_assertions(function, arguments):
    with pytest.raises(AssertionError):
        function(*arguments)
Das sind trotzdem zwei Testfälle. Der zweite wird also abgearbeitet auch wenn der erste fehlschlägt.

Re: Eine Funktion als Parameter übergeben mit beliebigen vielen Parametern

Verfasst: Mittwoch 6. November 2019, 09:23
von losgehts
Hallo __blackjack__,

vielen Dank für den Tipp! Dass werde ich mir anschauen.

Viele Grüße,
Ulrich