Teste de valores de limite inferior e superior em Python
Funcionalidade de código
O código a seguir testa se os valores que um usuário especifica para um limite inferior e superior em duas propriedades:
- O limite inferior é menor do que o limite superior.
- Os valores são maiores que 0 (positivo).
Uma vez que primeiro verifica se o limite inferior é menor do que o limite superior, ele verifica implicitamente se o limite superior é maior do que 0, se testar se o limite inferior é maior do que 0. Portanto, minha consideração é: posso tornar o código mais Compacte omitindo a verificação de "é o limite superior maior que 0".
Código
# 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")
Escolha de design
No entanto, se o código for modificado, pode não ser óbvio que o limite superior também precisa ser verificado porque isso é feito implicitamente. Isso pode resultar no caso de limite com limite superior abaixo de zero não ser detectado após as modificações. Portanto, para evitar esse cenário, posso implementar dois testes de unidade que verificam se um erro é gerado para:
- O limite inferior negativo, limite superior negativo
- O limite inferior zero, limite superior negativo
- O limite inferior positivo, o limite superior negativo
Questão
É recomendado incluir a verificação explícita no código principal de qualquer maneira, mesmo que seja testada nos testes de unidade?
Respostas
Exceto em circunstâncias incomuns, classes são coisas ou entidades - daí o termo objetos - enquanto funções ou métodos são ações ou operações. Você deseja nomeá-los de acordo. Por esse motivo, Get_labelsme parece uma classe com um nome estranho. Com base no que você nos mostrou, posso sugerir o nome Boundscomo alternativa. Um benefício colateral desse nome é que ele permite encurtar os nomes dos atributos sem perda de significado.
Um método separado para verificar a validade básica dos limites parece um excesso de engenharia para mim - a menos que a lógica de verificação se torne muito mais complexa ou a menos que seja usada em outra parte do código. Então, eu faria apenas uma validação simples __init__()neste caso.
Evite a tentação de usar mensagens tagarelas ou prolixas em seu código. Não vai ajudar você ou seus usuários a longo prazo - pelo menos é a minha experiência. Mantenha as coisas diretas e rigorosamente concisas. Na verdade, muitas vezes é benéfico manter as mensagens muito técnicas em vez de naturais em sua orientação estilística. O que quero dizer com isso é que, em vez de descrever o problema da maneira que alguém poderia verbalmente para um humano ("O limite inferior, que era 1000, deve ser menor do que o limite superior, que é 125"), muitas vezes é melhor descrever o problema de uma forma estereotipada, esquemática, semelhante a um computador. Entre outras coisas, essa abordagem permite que você adote um formato convencional para todas as mensagens de erro em um aplicativo. O formato da mensagem de erro mostrado na reescrita abaixo pode ser descrito genericamente como PROBLEM: SELF. Uma abordagem consistente torna mais fácil escrever o código de validação em primeiro lugar e mantê-lo ao longo do tempo. A consistência também transmite profissionalismo aos usuários.
Ao longo dessas linhas, muitas vezes você pode simplificar a criação de tais mensagens de validação, primeiro definindo __repr__()para sua classe, conforme ilustrado abaixo.
Para as validações que você tem até agora, a ValueErroré mais adequado do que levantar um geral Exception. Além disso, você pode considerar a verificação de outros tipos de erros: por exemplo, os limites são restritos apenas a inteiros?
Se você fizer check, aumente a TypeError.
Finalmente, um ponto fino estilístico e reconhecidamente subjetivo. Abaixo está um rastreamento de pilha de seu código conforme escrito. Vemos a mensagem detalhada duas vezes, primeiro como uma string f e, em seguida, com os parâmetros preenchidos. O que há de errado nisso? Nada sério, mas é pesado, tedioso, até mesmo sem uma certa elegância. No mínimo, pode-se dizer que a repetição da mensagem prolixa distrai levemente o usuário, aumentando a carga visual ou cognitiva do usuário para descobrir o que está acontecendo. Compare isso com o rastreamento de pilha do código revisado.
# 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)
Código com algumas edições possíveis para você considerar:
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})'