[Python 3] Testare prezenta exceptie odata ce o anume conditie a fost indeplinita legata de un argument al unui program cli

Salutare! Incerc sa fac un program cli in python care sa monitorizeze un proces la un anumit interval de timp, insa m-am lovit de o problema.

Mai exact, cum as putea sa testez că exceptia ValueError a fost chemata odata ce a fost folosita o valoare negativa pentru argumentul timp?

Am facut o incercare nereusita care poate fi regasita in functia test_valoare_negativa din test_exemplu.py.
M-as bucura daca raspunsul vostru ar putea respecta structura regasita in ambele fisiere de mai jos. Daca acest lucru nu este posibil, atunci orice fel de raspuns este bine-venit.

Daca aveti neclaritati, va rog sa mi le adresati!
Va multumesc!

Am folosit:

Python: 3.10.4
argparse: 1.1

Aici este codul pentru exemplu.py si test_exemplu.py:


# exemplu.py

import argparse, sys

def parse_args(args):
    parser = argparse.ArgumentParser()
    parser.add_argument("-c", "--cale", type=str, metavar=" ")
    parser.add_argument("-t", "--timp", type=float, metavar=" ")
    return parser.parse_args(args)

def main():
    parser = parse_args(sys.argv[1:])
    if parser.timp < 0:
        raise ValueError(f"Timpul are o valoare negativa: {parser.timp}. Te rog introdu o valoare pozitiva") 

    print(parser.cale)
    print(parser.timp)

    # aici vine restul programului        


if __name__ == '__main__':
    main()

# test_exemplu.py

import unittest
from exemplu import parse_args

class TestExemplu(unittest.TestCase):
    def test_parser(self):       
        parser = parse_args(['-c', 'asd', '-t', '-1'])
        self.assertEqual(parser.cale,'asd')
        self.assertEqual(parser.timp, -1)    
    
    # asta e incercarea esuata pe care am facut-o
    def test_valoare_negativa(self):
       parser = parse_args(['-t', '-1'])
       self.assertRaises(ValueError, parser.timp, -1) 


if __name__ == '__main__':
    unittest.main()

1 Like

Ce ai implementat tu este cod greu de testat pentru ca ai doua comportamente (parsare, validare), iar partea de validare e facuta ad-hoc in main, nu e extrasa intr-o functie care poate fi usor testat.

Recomandarea mea e sa ai o singura functie care face parsare + validare si returneaza Success(c, t) | ValidationError, sau arunca exceptie (bleah!)

Deci, ori implementezi o functie care face validare si o testezi pe aia, ori Parse, don’t validate :slight_smile:

1 Like

Iti multumesc! Din pacate articolul pe care mi l-ai dat nu prea ma ajuta intrucat sunt incepator.

Daca nu te superi, ai putea sa-mi dai un exemplu concret in ceea ce priveste cele doua fisiere, bazat pe recomandarea ta?

import argparse, sys

def parse_args(args):
    parser = argparse.ArgumentParser()
    parser.add_argument("-c", "--cale", type=str, metavar=" ")
    parser.add_argument("-t", "--timp", type=float, metavar=" ")
    return parser.parse_args(args)

def validate(data):
    if data.timp < 0:
        raise ValueError(f"Timpul are o valoare negativa: {data.timp}. Te rog introdu o valoare pozitiva") 

def main():
    parsed_data = parse_args(sys.argv[1:])
    validate(parsed_data)

    print(parser.cale)
    print(parser.timp)
import unittest
from exemplu import parse_args

class TestExemplu(unittest.TestCase):
    def test_parser(self):       
        parser = parse_args(['-c', 'asd', '-t', '-1'])
        self.assertEqual(parser.cale,'asd')
        self.assertEqual(parser.timp, -1)    
    
    def test_valoare_negativa(self):
       parsed_data = parse_args(['-t', '-1'])
       self.assertRaises(ValueError, validate, parsed_data)

In documentatia assertRaises (unittest — Unit testing framework — Python 3.10.4 documentation) iti zice cum trebuie folosit.
assertRaises(exception, callable, *args, **kwds)
exception - Exceptia pe care o astepti
callable - functia pe care vrei sa o testezi. Aici era problema ta, bucata de cod pe care voiai sa o testezi nu era extrasa intr-o functie.
args - parametrii care vor fi pasati lui callable

2 Likes

Iti multumesc foarte mult pentru ajutor si pentru explicatii! Functioneaza exact cum imi doream!

O zi faina! :slight_smile:

P.S:

A trebuit insa sa ajustez putin codul in ambele fisiere ca sa-l pot rula:


# exemplu.py

import argparse, sys

def parse_args(args):
    parser = argparse.ArgumentParser()
    parser.add_argument("-c", "--cale", type=str, metavar=" ")
    parser.add_argument("-t", "--timp", type=float, metavar=" ")
    return parser.parse_args(args)

def validate(data):
    if data.timp < 0:
        raise ValueError(f"Timpul are o valoare negativa: {data.timp}. Te rog introdu o valoare pozitiva") 
    
def main():
    parsed_data = parse_args(sys.argv[1:])
    validate(parsed_data)

    print(parsed_data.cale)
    print(parsed_data.timp)

if __name__ == '__main__':
    main()    

# test_exemplu.py

import unittest
from exemplu import parse_args, validate

class TestExemplu(unittest.TestCase):
    def test_parser(self):       
        parser = parse_args(['-c', 'asd', '-t', '-1'])
        self.assertEqual(parser.cale,'asd')
        self.assertEqual(parser.timp, -1)    
    
    def test_valoare_negativa(self):
       parsed_data = parse_args(['-t', '-1'])
       self.assertRaises(ValueError, validate, parsed_data)

if __name__ == '__main__':
    unittest.main()