Automação de Testes: Coleta, Renomeação e Geração de Casos de Teste

Este guia aborda a criação de um conjunto de scripts para gerenciar de forma automatizada uma base de testes existente. O cenário envolve uma estrutura de diretórios com múltiplos módulos e submódulos, onde os arquivos de teste podem apresentar inconsistências de nomenclatura, status e metadados.

Estrutura de Diretórios e Cenário Atual

Os casos de teste estão organizados em um diretório raiz testcases, subdividido em módulos e submódulos. Cada script .py contém uma única classe de teste. Problemas comuns incluem nomes de arquivos com símbolos (✅/❌), discrepâncias entre nome do arquivo e nome da classe, e metadados (como prioridade) inconsistentes.

Exemplo de Script de Teste

Cada teste segue um padrão definido por uma classe que herda de TestCase. Os metadados são definidos como atributos de classe.

# Arquivo: sample_test.py
from testbase import TestCase

class SampleTest(TestCase):
    """Exemplo de caso de teste automatizado"""
    description = ''
    owner = 'developer'
    priority = TestCase.EnumPriority.Medium
    status = TestCase.EnumStatus.Ready
    tags = ("exemplo", "módulo1")
    timeout = 2

    def run_test(self):
        """Passos:
        1. Realizar ação principal.
        2. Verificar resultado esperado.
        """
        self.start_step('Ação principal')
        # Implementação do passo 1

if __name__ == '__main__':
    SampleTest().debug_run()

Scripts de Automação para Gestão

1. Estatísticas de Casos de Teste

Este script percorre a estrutura de diretórios e coleta informações das classes de teste, gerando contagens por módulo e por responsável.

import os
import importlib
import inspect
from collections import defaultdict

def collect_test_cases(root_dir):
    module_stats = defaultdict(int)
    owner_stats = defaultdict(int)
    
    for dirpath, _, filenames in os.walk(root_dir):
        for file in filenames:
            if file.endswith('.py') and file != '__init__.py':
                rel_path = os.path.relpath(os.path.join(dirpath, file), root_dir)
                module_path = rel_path.replace(os.sep, '.').rstrip('.py')
                try:
                    mod = importlib.import_module(f'testcases.{module_path}')
                    for name, obj in inspect.getmembers(mod, inspect.isclass):
                        if issubclass(obj, 'TestCase') and obj is not 'TestCase':
                            module = os.path.dirname(rel_path)
                            owner = getattr(obj, 'owner', 'Não definido')
                            module_stats[module] += 1
                            owner_stats[owner] += 1
                except Exception as e:
                    print(f"Erro ao importar {module_path}: {e}")
    return module_stats, owner_stats

2. Limpeza de Nomes de Arquivos

Remove marcadores de status (✅,❌) dos nomes dos arquivos de teste.

import os
import re

def clean_filenames(root_dir):
    for dirpath, _, filenames in os.walk(root_dir):
        for filename in filenames:
            if filename.endswith('.py'):
                cleaned_name = re.sub(r'[✅❌]', '', filename)
                if cleaned_name != filename:
                    old_path = os.path.join(dirpath, filename)
                    new_path = os.path.join(dirpath, cleaned_name)
                    os.rename(old_path, new_path)
                    print(f"Renomeado: {filename} -> {cleaned_name}")

3. Sincronização de Nomes de Classes

Altera o nome da classe dentro do script para corresponder ao nome do arquivo (convertido de snake_case para PascalCase).

import os
import re

def snake_to_pascal(name):
    return ''.join(word.capitalize() for word in name.split('_'))

def fix_class_names(root_dir):
    for dirpath, _, filenames in os.walk(root_dir):
        for filename in filenames:
            if filename.endswith('.py') and filename != '__init__.py':
                file_class_name = snake_to_pascal(os.path.splitext(filename)[0])
                filepath = os.path.join(dirpath, filename)
                with open(filepath, 'r') as f:
                    content = f.read()
                
                # Encontra e substitui o nome da classe no código
                pattern = r'^class\s+\w+\(TestCase\):'
                match = re.search(pattern, content, re.MULTILINE)
                if match:
                    old_class = match.group()
                    new_class = f'class {file_class_name}(TestCase):'
                    new_content = content.replace(old_class, new_class)
                    if new_content != content:
                        with open(filepath, 'w') as f:
                            f.write(new_content)
                        print(f"Corrigido: {filename}")

4. Marcação de Casos em Imlpementação

Renomeia scripts com status Implement para incluir sufixo _implement.

import os
import importlib

def mark_implement_status(root_dir):
    for dirpath, _, filenames in os.walk(root_dir):
        for filename in filenames:
            if filename.endswith('.py') and not filename.endswith('_implement.py'):
                rel_path = os.path.relpath(os.path.join(dirpath, file), root_dir)
                module_path = rel_path.replace(os.sep, '.').rstrip('.py')
                try:
                    mod = importlib.import_module(f'testcases.{module_path}')
                    for name, obj in inspect.getmembers(mod, inspect.isclass):
                        if hasattr(obj, 'status') and obj.status == 'TestCase.EnumStatus.Implement':
                            base_name = os.path.splitext(filename)[0]
                            new_filename = f"{base_name}_implement.py"
                            os.rename(
                                os.path.join(dirpath, filename),
                                os.path.join(dirpath, new_filename)
                            )
                            print(f"Marcado: {filename} -> {new_filename}")
                            break
                except Exception:
                    pass

5. Geração de Casos a partir de Planilha

Cria scripts de teste no formato padrão com base em uma lista de casos de teste em formato CSV.

import os
import csv

TEMPLATE = '''from testbase import TestCase

class {classname}(TestCase):
    """{title}"""
    owner = "{owner}"
    priority = TestCase.EnumPriority.{priority}
    status = TestCase.EnumStatus.Implement

    def run_test(self):
        """Passos:
        {steps}
        """
        pass
'''

def generate_test_cases(csv_path, target_dir):
    os.makedirs(target_dir, exist_ok=True)
    with open(csv_path, 'r', encoding='utf-8') as f:
        reader = csv.DictReader(f)
        for row in reader:
            classname = row['nome_classe']
            filename = f"{classname.lower()}.py"
            filepath = os.path.join(target_dir, filename)
            
            steps = '\n'.join([f'        {i+1}. {step}' for i, step in enumerate(row['passos'].split('\\n'))])
            
            with open(filepath, 'w') as f:
                f.write(TEMPLATE.format(
                    classname=classname,
                    title=row['titulo'],
                    owner=row['responsavel'],
                    priority=row['prioridade'],
                    steps=steps
                ))
            print(f"Gerado: {filename}")

6. Exportação de Metadados para CSV

Coleta metadados de todos os casos de teste e exporta para um arquivo CSV.

import os
import csv
import importlib
import inspect

def export_test_metadata(root_dir, output_file):
    with open(output_file, 'w', newline='', encoding='utf-8') as csvfile:
        writer = csv.writer(csvfile)
        writer.writerow(['Classe', 'Título', 'Módulo', 'Prioridade', 'Status', 'Responsável', 'Caminho'])
        
        for dirpath, _, filenames in os.walk(root_dir):
            for filename in filenames:
                if filename.endswith('.py') and filename != '__init__.py':
                    rel_path = os.path.relpath(os.path.join(dirpath, filename), root_dir)
                    module = os.path.dirname(rel_path)
                    module_path = rel_path.replace(os.sep, '.').rstrip('.py')
                    
                    try:
                        mod = importlib.import_module(f'testcases.{module_path}')
                        for name, obj in inspect.getmembers(mod, inspect.isclass):
                            if hasattr(obj, 'run_test'):
                                writer.writerow([
                                    name,
                                    getattr(obj, '__doc__', ''),
                                    module,
                                    getattr(obj, 'priority', ''),
                                    getattr(obj, 'status', ''),
                                    getattr(obj, 'owner', ''),
                                    rel_path
                                ])
                    except Exception as e:
                        print(f"Erro ao processar {module_path}: {e}")

Tags: Python testautomation qtaf filemanagement codegeneration

Publicado em 7-18 07:34