Testare i valori limite inferiore e superiore in Python
Funzionalità del codice
Il codice seguente verifica se i valori che un utente specifica per un limite inferiore e un limite superiore su due proprietà:
- Il limite inferiore è più piccolo del limite superiore.
- I valori sono maggiori di 0 (positivo).
Poiché controlla prima se il limite inferiore è più piccolo del limite superiore, verifica implicitamente che il limite superiore sia maggiore di 0, se verifica se il limite inferiore è maggiore di 0. Pertanto, la mia considerazione è: posso rendere il codice più compatta omettendo il segno di spunta per "è il limite superiore maggiore di 0".
Codice
# Object getting labels
class Get_labels:
def __init__(self,lower_bound,upper_bound,configuration_name):
self.lower_bound = lower_bound
self.upper_bound = upper_bound
self.configuration_name = configuration_name
self.check_threshold_validity()
# Verifies if the chosen thresholds are valid values.
def check_threshold_validity(self):
if self.lower_bound>=self.upper_bound:
raise Exception(f'Sorry, the lower threshold={self.lower_bound} should be smaller than the upper bound={self.upper_bound} for configuration={self.configuration_name}')
# checks if lower bound (and implicitly upper bound) are above zero
if self.lower_bound<=0:
raise Exception(f'Sorry, the lower threshold={self.lower_bound} should be larger than 0 for configuration={self.configuration_name}')
if __name__ == '__main__':
get_labels = Get_labels(-1,25,"first")
Scelta progettuale
Tuttavia, se il codice viene modificato, potrebbe non essere ovvio che anche il limite superiore deve essere verificato perché ciò viene fatto implicitamente. Ciò potrebbe comportare che il caso limite con limite superiore inferiore a zero non venga catturato dopo le modifiche. Quindi per evitare questo scenario, posso implementare due unit test che controllano se viene generato un errore per:
- Il limite inferiore negativo, il limite superiore negativo
- Il limite inferiore zero, il limite superiore negativo
- Il limite inferiore positivo, il limite superiore negativo
Domanda
Si consiglia comunque di includere il check esplicito nel codice principale, anche se viene testato negli unit test?
Risposte
Tranne che in circostanze insolite, le classi sono cose o entità - da cui il termine oggetti - mentre funzioni o metodi sono azioni o operazioni. Si desidera denominarli di conseguenza. Per questo motivo, Get_labelsmi sembra una classe dal nome strano. Sulla base di quello che ci hai mostrato, potrei suggerire il nome Boundscome alternativa. Un vantaggio collaterale di quel nome è che consente di abbreviare i nomi degli attributi senza perdere di significato.
Un metodo separato per verificare la validità di base dei limiti mi sembra eccessivo, a meno che la logica di controllo non diventi molto più complessa oa meno che non venga utilizzata altrove nel codice. Quindi __init__()in questo caso farei solo una semplice convalida .
Evita la tentazione di usare messaggi chiacchieroni o verbosi nel tuo codice. Non aiuterà né te né i tuoi utenti a lungo termine, almeno questa è la mia esperienza. Mantieni le cose dirette e rigorosamente concise. In effetti, è spesso utile mantenere i messaggi molto tecnici piuttosto che naturali nel loro orientamento stilistico. Quello che intendo dire è piuttosto che descrivere il problema nel modo in cui si potrebbe verbalmente a un essere umano ("Il limite inferiore, che era 1000, deve essere inferiore al limite superiore, che è 125"), è spesso meglio descrivere il problema in modo stereotipato, schematico, computerizzato. Tra le altre cose, questo approccio consente di adottare un formato convenzionale per tutti i messaggi di errore in un'applicazione. Il formato del messaggio di errore mostrato nella riscrittura di seguito potrebbe essere descritto genericamente come PROBLEM: SELF. Un approccio coerente rende più facile scrivere codice di convalida in primo luogo e mantenerlo nel tempo. La coerenza trasmette anche professionalità agli utenti.
In questo senso, puoi spesso semplificare la creazione di tali messaggi di convalida definendoli prima __repr__()per la tua classe, come illustrato di seguito.
Per le convalide che hai finora, un ValueErrorè più adatto che sollevare un generale Exception. Inoltre, potresti prendere in considerazione la verifica di altri tipi di errori: ad esempio, i limiti sono limitati solo a numeri interi?
Se controlli, rilancia a TypeError.
Infine, un bel punto stilistico e dichiaratamente soggettivo. Di seguito è riportata una traccia dello stack dal codice come scritto. Vediamo il messaggio dettagliato due volte, prima come una stringa f, e poi con i parametri inseriti. Cosa c'è di sbagliato in questo? Niente di grave, ma è pesante, noioso, manca anche di una certa eleganza. Come minimo, si potrebbe dire che la ripetizione del messaggio prolisso distrae leggermente l'utente, mettendo un ulteriore incremento di un carico visivo o cognitivo sull'utente per capire cosa sta succedendo. Confrontalo con la traccia dello stack dal codice rivisto.
# ORIGINAL.
Traceback (most recent call last):
File "bounds.py", line 19, in <module>
get_labels = Get_labels(-1,25,"first")
File "bounds.py", line 7, in __init__
self.check_threshold_validity()
File "bounds.py", line 17, in check_threshold_validity
raise Exception(f'Sorry, the lower threshold={self.lower_bound} should be larger than 0 for configuration={self.configuration_name}')
Exception: Sorry, the lower threshold=-1 should be larger than 0 for configuration=first
# REVISED.
Traceback (most recent call last):
File "bounds.py", line 72, in <module>
b1 = Bounds(1000, 125, 'first')
File "bounds.py", line 67, in __init__
raise Exception(msg)
Exception: Upper bound must be greater than lower: Bounds(1000, 125, first)
Codice con alcune possibili modifiche da considerare:
class Bounds:
def __init__(self, lower, upper, name):
self.lower = lower
self.upper = upper
self.name = name
if lower <= 0 or upper <= 0:
msg = f'Bounds must be positive: {self}'
raise ValueError(msg)
if upper <= lower:
msg = f'Upper bound must be greater than lower: {self}'
raise ValueError(msg)
def __repr__(self):
return f'Bounds({self.lower}, {self.upper}, {self.name!r})'