O KLARA é uma ferramenta de análise estática para o caso de teste de geração automática, com base no solucionador de SMT (Z3), com um poderoso sistema de inferência de nível AST. Klara tomará o arquivo python como entrada e gerará arquivo de teste correspondente no formato Pytest, que tentam cobrir todos os valores de retorno. Por exemplo, a função seguinte no arquivo test.py
def triangle ( x : int , y : int , z : int ) -> str :
if x == y == z :
return "Equilateral triangle"
elif x == y or y == z or x == z :
return "Isosceles triangle"
else :
return "Scalene triangle"irá gerar
import test
def test_triangle_0 ():
assert test . triangle ( 0 , 0 , 0 ) == 'Equilateral triangle'
assert test . triangle ( 0 , 0 , 1 ) == 'Isosceles triangle'
assert test . triangle ( 2 , 0 , 1 ) == 'Scalene triangle'Veja a documentação da Klara em https://klara-py.readthedocs.io
Nota : Klara ainda está em estágio experimental inicial, características ausentes notáveis são loop, compreensão, importação de módulos, exceções e muito mais. Veja limitações para a lista completa. Provavelmente não será executado em projetos do mundo real, por isso é melhor escolher algumas funções interessantes para gerar o caso de teste correspondente.
Klara pode ser instalado via ferramenta pip usando:
pip install klara
Podemos invocar klara em qualquer arquivo de origem do Python e ele gerará um arquivo de teste pytest correspondente.
$ cat source.py
def foo(x: int, y: int, z: str):
if x + y > 2:
return x + y + 12
elif x < y:
return x + y
elif (z + " me " ) == " some " :
return z + " thing "
else:
return x - y
$ klara source.py
$ cat test_source.py
import contract_test
def test_foo_0 ():
assert contract_test.foo(0, 3, '' ) == 15
assert contract_test.foo(0, 1, '' ) == 1
assert contract_test.foo(0, 0, ' so ' ) == ' sothing '
assert contract_test.foo(0, 0, '' ) == 0Consulte o Manual de Iniciar Quick para obter mais exemplos e orientações. Para usá -lo como uma biblioteca de análise estática, vá para a inferência.
O KLARA funciona no nível AST e não executa o código do usuário de forma alguma, o que é uma diferença muito importante em comparação com uma ferramenta semelhante, como crosshair e pynguin, que utilizam a execução simbólica concólica que exigia a execução do código do usuário que pudesse causar efeitos colaterais indesejados. Klara trabalha no nível AST, combine com a análise de fluxo de dados que utiliza o gráfico de fluxo de controle (CFG), atribuição única estática (SSA), cadeia de uso de uso, etc ... para construir um poderoso sistema de inferência de Python que aproveita o Solver Z3 para restrições de resolução e verificação de viabilidade de caminho. Por esse motivo, a Klara é capaz de operar no código -fonte Python2/3 com a ajuda do typed_ast. Para especificar o código -fonte, está no Python 2, passe no argumento -py 2 . É Python 3 por padrão.
O KLARA também pode ser usado como uma ferramenta de análise estática, permitindo que o usuário definir a regra personalizada para identificar erros de programação, erro ou aplicar o padrão de codificação. Com o suporte ao solucionador SMT, a análise será mais precisa e reduzirá bastante o caso falso-positivo. Por exemplo
import klara
tree = klara . parse ( """
def foo(v1: int):
if v1 > 4:
if v1 < 3:
z = 1
else:
z = 2
else:
z = 3
s = z
""" )
with klara . MANAGER . initialize_z3_var_from_func ( tree . body [ 0 ]):
print ( list ( tree . body [ 0 ]. body [ - 1 ]. value . infer ()))Imprimirá:
[2, 3]
Porque z = 1 não é possível devido a v1 > 4 e v1 < 3 é insatisfatório
A arquitetura do sistema de inferência e a API é amplamente inspirada pelo astroid, uma biblioteca de inferência estática usada pelo Pylint.
Klara utiliza o sistema de inferência para gerar caso de teste, em outras palavras, gera um caso de teste para todos os possíveis valores de retorno da função , em vez de gerar caso de teste para todo o caminho de controle da função.
Para ilustrar o ponto, considere a função abaixo, com divide by zero vulnerabilidades na linha 3
def foo ( v1 : int , v2 : float ):
if v1 > 10000 :
s = v1 / 0 # unused statement
if v1 > v2 :
s = v1
else :
s = v2
return sKlara gerará entradas de teste abaixo
import contract_test
def test_foo_0 ():
assert contract_test . foo ( 0 , - 1.0 ) == 0
assert contract_test . foo ( 0 , 0.0 ) == 0.0 Ele não gera entrada v1 > 10000 , portanto, o caso de teste não seria capaz de descobrir as exceções. Isso ocorre porque o s na linha 3 não é utilizado no valor de retorno.
Se modificarmos a segunda instrução IF para elif , que poderemos retornar os [s] {. Title-ref} na linha 3, Klara gerará entradas de teste que cobrem o caso v1 > 10000 .
Essa é uma distinção importante com outra geração automática de casos de teste disponível agora, porque, apenas gerando um caso de teste para os valores de retorno, podemos gerar um caso de teste mínimo e é mais fácil personalizar como o Klara cobre a função.
Por exemplo, digamos que estamos compondo um sistema complexo
def main ( number : int , cm : int , dc : int , wn : int ):
mc = 0
if wn > 2 :
if number > 2 and number > 2 or number > 2 :
if number > 0 :
if wn > 2 or wn > 2 :
mc = 2
else :
mc = 5
else :
mc = 100
else :
mc = 1
nnn = number * cm
if cm <= 4 :
num_incr = 4
else :
num_incr = cm
n_num_incr = nnn / num_incr
nnn_left = dc * num_incr * ( n_num_incr / 2 + n_num_incr % 2 )
nnn_right = nnn - nnn_left
is_flag = nnn_right
if is_flag :
cell = Component ( nnn_right , options = [ mc ])
else :
cell = Component ( nnn_right )
return cellNão está imediatamente claro para nós quantos possíveis valores de retorno existem. Mas podemos utilizar Klara para gerar entradas instantaneamente, abaixo está o teste gerado
import contract_test
def test_main_0 ():
assert contract_test . main ( 2 , 4 , 1 , 3 ) is not None
assert contract_test . main ( 2 , 4 , - 1 , 6 ) is not None
assert contract_test . main ( 2 , 4 , 1 , 4 ) is not None
assert contract_test . main ( - 2 , 4 , 3 , 4 ) is not None
assert contract_test . main ( - 1 , - 1 , - 1 , 2 ) is not None
assert contract_test . main ( 0 , 0 , 0 , 3 ) is not None
assert contract_test . main ( 0 , 0 , 0 , 6 ) is not None
assert contract_test . main ( 0 , 0 , 0 , 4 ) is not None
assert contract_test . main ( - 2 , 0 , 0 , 4 ) is not None
assert contract_test . main ( 0 , 0 , 0 , 0 ) is not None Acima de 10 resultados totais, que é o produto de nnn_right , que possui 2 possibilidades e mc que têm 5 possibilidades.
Suponha que 10 testes a entrada seja demais e determinamos que o argumento options para Component seja redundante para testar, podemos usar o plug -in personalizado da Klara para determinar seletivamente qual parte ignorar na geração de testes. Vá para personalizar a estratégia de cobertura para obter mais informações.
Depois de configurarmos o plug -in, Klara gerará o teste seguinte
import contract_test
def test_main_0 ():
assert contract_test . main ( 1 , 3 , 0 , 0 ) is not None
assert contract_test . main ( 0 , 0 , 0 , 0 ) is not None Que são apenas 2 combinações de nnn_right
Como Klara não pode executar dinamicamente o código, ele fornecerá extensão para especificar como inferir um nó AST específico ou tipo definido pelo usuário para tornar Klara 'mais inteligente'. É descrito ao estender, estender o tipo de usuário e personalizar a estratégia de cobertura.
Usamos poesia para gerenciar dependências. Após a instalação da poesia, execute:
$ poetry shell
$ poetry install
Para executar o caso de teste, faça:
$ poetry run pytest test
Este projeto é licenciado sob os termos da Licença Pública Geral GNU menor.