Come testare con Python unittest che un avvertimento è stato lanciato?

Ho un seguente funzione in Python e vorrei provare con unittest che se la funzione ottiene 0 come argomento, si lancia un messaggio di avviso. Ho già provato assertRaises, ma dal momento che io non aumentare l’avviso, che non funziona.

def isZero(i):
    if i != 0:
        print "OK"
    else:
        warning = Warning("the input is 0!") 
        print warning
    return i
  • Per quanto riguarda il lavoro attorno …se un messaggio di avviso è già stato sollevato a causa di una regola di default, quindi non importa ciò che i filtri sono impostati l’avvertimento di non essere visto di nuovo, a meno che gli avvisi del registro di sistema correlati a tale avviso è stato cancellato. (documenti) vedi questo articolo circa il livello di modulo __warningregistry__ (il registro di sistema docs cita).

 

4 Replies
  1. 48

    È possibile utilizzare il catch_warnings contesto manager. In sostanza, questo permette di deridere gli avvisi del gestore, in modo che è possibile verificare i dettagli dell’avviso. Vedere la documentazione ufficiale per una spiegazione più completa e di esempio di codice di test.

    import warnings
    
    def fxn():
        warnings.warn("deprecated", DeprecationWarning)
    
    with warnings.catch_warnings(record=True) as w:
        # Cause all warnings to always be triggered.
        warnings.simplefilter("always")
        # Trigger a warning.
        fxn()
        # Verify some things
        assert len(w) == 1
        assert issubclass(w[-1].category, DeprecationWarning)
        assert "deprecated" in str(w[-1].message)
    • Prendere nota che questo NON è thread-safe, perché modifica di un global state – se si utilizza questo in una suite di test e di altri avvisi vengono emessi, questi saranno visualizzati anche in catch_warnings, che può causare falsi negativi.
  2. 41

    Di iniziare con Python 3.2, si può semplicemente utilizzare assertWarns() metodo.

    with self.assertWarns(Warning):
        do_something()
    • Questo funziona indipendentemente dall’avviso di filtri attivi. Pertanto, vorrei utilizzare questa risposta, piuttosto che il accettato uno.
    • C’è un simile modo di affermare che un avvertimento non è stato attivato o vorresti che richiedono cadendo indietro sul beffardo o warnings.catch_warnings?
    • … o l’esecuzione di self.assertWarns() ma attaccare un @unittest.expectedFailure sul test.
  3. 18

    È possibile scrivere il proprio assertWarns funzione incapsula catch_warnings contesto. Ho appena implementato modo seguente, con un mixin:

    class WarningTestMixin(object):
        'A test which checks if the specified warning was raised'
    
        def assertWarns(self, warning, callable, *args, **kwds):
            with warnings.catch_warnings(record=True) as warning_list:
                warnings.simplefilter('always')
    
                result = callable(*args, **kwds)
    
                self.assertTrue(any(item.category == warning for item in warning_list))

    Un esempio di utilizzo:

    class SomeTest(WarningTestMixin, TestCase):
        'Your testcase'
    
        def test_something(self):
            self.assertWarns(
                UserWarning,
                your_function_which_issues_a_warning,
                5, 10, 'john', # args
                foo='bar'      # kwargs
            )

    Il test sarà superato se almeno uno degli avvisi emessi da your_function è di tipo UserWarning.

  4. 6

    @ire_and_curses’ risposta è molto utile e penso che canonical. Ecco un altro modo per fare la stessa cosa. Questo richiede Michael Foord eccellente Mock biblioteca.

    import unittest, warnings
    from mock import patch_object
    
    def isZero( i):
       if i != 0:
         print "OK"
       else:
         warnings.warn( "the input is 0!")
       return i
    
    class Foo(unittest.TestCase):
        @patch_object(warnings, 'warn')
        def test_is_zero_raises_warning(self, mock_warn):
            isZero(0)
            self.assertTrue(mock_warn.called)
    
    if __name__ == '__main__':
        unittest.main()

    I nifty patch_object permette di deridere il warn metodo.

    • +1 – Questa soluzione è migliore rispetto accettato di rispondere, in quanto non uso lo stato globale del warnings biblioteca – che può causare falsi negativi.
    • Questa è una risposta ragionevole, ma l’affermazione nelle versioni più recenti di mock biblioteca sarebbe meglio in questo modo, se non siete ancora su Python 3 e non è possibile utilizzare assertWarns: mock_warn.assert_called_once(). Questo sarebbe prendere casi in cui qualche altro modulo random generato in modo imprevisto un messaggio di avviso.

Lascia un commento