Abordagens de testes: Múltiplos vs. Único assert

Banner

Durante meus estudos sobre testes unitários usando pytest, me deparei com o vídeo do Eduardo Mendes "Live de Python #167". Nele, o Youtuber ressaltou a importância dos testes serem isolados, stateless e unitários, o que despertou minha atenção e me motivou a aprofundar meus conhecimentos nessa área.

Na busca por referências sólidas, decidi explorar repositórios estabelecidos no GitHub e encontrei situações semelhantes, como exemplificado no código a seguir: Python

def test_calculate_total():
    # Define os inputs
    items = ['item1', 'item2', 'item3']
    prices = [10, 20, 30]

    # Checagem 1: Verifica se o tamanho de items e prices é o mesmo
    assert len(items) == len(prices)

    # Executa a função que estamos testando
    total = calculate_total(items, prices)

    # Checagem 2: Verifica se o tipo do valor retornado é float
    assert isinstance(total, float)

    # Checagem 3: Verifica se o valor total calculado está correto
    assert total == sum(prices)

Após uma extensa pesquisa sobre o tema, constatei que havia uma escassez de informações disponíveis. Para obter uma perspectiva embasada, entrei em contato com um amigo que possui mais de 15 anos de experiência como dev e pedi sua opinião. Ele compartilhou comigo suas percepções, as quais serão apresentadas a seguir. Dada a falta de abordagens diretas sobre o assunto em português, decidi compartilhá-las aqui no TabNews, acreditando que minha indagação possa ser compartilhada por outros leitores.

Quando discutimos o uso de declarações assert em testes unitários, encontramos uma variedade de práticas recomendadas. É importante analisar diferentes perspectivas e considerações relevantes:

  1. Múltiplos asserts em um único método de teste: Uma abordagem é ter múltiplas declarações assert em um único método de teste. Isso pode ser observado em muitos exemplos em plataformas como o GitHub. A vantagem dessa abordagem é que ela permite testar várias condições ou aspectos de uma função ou recurso em um único caso de teste. Por exemplo, em um caso de teste para uma função "testCalculateSum", você pode ter múltiplos asserts para testar diferentes entradas e saídas esperadas. Essa abordagem pode ser prática e eficiente, desde que os asserts sejam logicamente relacionados e não dependam dos efeitos colaterais da execução anterior. Python
def testCalculateSum():
    # Testando a soma de dois números positivos
    assert calculateSum(2, 3) == 5

    # Testando a soma de um número positivo e zero
    assert calculateSum(10, 0) == 10

    # Testando a soma de dois números negativos
    assert calculateSum(-5, -7) == -12
  1. Um assert por método de teste: Outra abordagem é ter apenas uma declaração assert por método de teste. Isso é defendido por alguns desenvolvedores como uma prática recomendada. A ideia por trás dessa abordagem é ter cada método de teste testando uma única unidade isolada, seguindo o padrão "arrange, act, assert". A vantagem dessa abordagem é que torna mais fácil identificar a causa de uma falha quando um caso de teste falha. Se você tiver múltiplos asserts em um único método de teste e um deles falhar, pode ser mais difícil determinar qual condição específica causou a falha. Ao ter apenas um assert por método de teste, você pode identificar rapidamente a condição que falhou. Python
def test_sum_positive_numbers():
    # Arrange
    a = 2
    b = 3
    
    # Act
    result = calculateSum(a, b)
    
    # Assert
    assert result == 5
  1. Testar um conceito lógico por teste: Uma variação da abordagem de um assert por método de teste é testar um conceito lógico por teste. Isso significa que você pode ter múltiplos asserts em um método de teste, desde que eles sejam relacionados e estejam vinculados ao mesmo conceito sendo testado. Por exemplo, se você tiver um método de teste chamado GetDocuments_returns_list_of_documents, você pode ter vários asserts que testam diferentes aspectos da lista retornada, como a nulidade, a ausência de elementos e a validade do primeiro elemento. Python
def test_GetDocuments_returns_list_of_documents():
    # Arrange
    documents = GetDocuments()
    
    # Asserts
    assert documents is not None
    assert len(documents) > 0
    assert isinstance(documents[0], Document)

Banner Em resumo, não há uma resposta definitiva sobre se é preferível ter apenas um assert por método de teste. Ambas as abordagens têm seus méritos e desvantagens. Aqui estão algumas considerações para ter em mente:

  • Ter múltiplos asserts em um único método de teste pode ser prático e eficiente, especialmente quando os asserts são logicamente relacionados e não dependem dos efeitos colaterais da execução anterior. Isso permite testar várias condições ou aspectos de uma função ou recurso em um único caso de teste.
  • Ter um assert por método de teste facilita a identificação da causa de uma falha quando um caso de teste falha. Seguir o padrão "arrange, act, assert" ajuda a manter o isolamento dos testes.
  • Se você optar por ter múltiplos asserts em um método de teste, certifique-se de que eles estejam relacionados e testem o mesmo conceito lógico. Isso pode melhorar a legibilidade e a manutenibilidade dos seus testes.
  • No final, a decisão de ter múltiplos asserts ou um assert por método de teste depende do contexto específico, da complexidade do código sendo testado e das preferências da equipe de desenvolvimento ou da organização.

Vale ressaltar que a escolha da abordagem pode variar entre diferentes empresas e projetos. Algumas empresas ou equipes podem ter diretrizes, ou convenções específicas sobre o uso de declarações "assert" em testes unitários. É sempre uma boa ideia consultar sua equipe ou líder de projeto para determinar a abordagem preferida em seu contexto específico. Sinta-se à vontade para discordar ou me corrigir sobre o assunto. Meu objetivo principal é ajudar e ser ajudado.

Fontes:

  1. Você ainda não sabe como fazer testes de unidade
  2. Testes Unitários 101: Mocks, Stubs, Spies e todas essas palavras difíceis
  3. Melhores práticas de teste de unidade com .NET Core e .NET Standard
  4. Eduardo Mendes - Youtube - Pytest: Uma introdução - Live de Python #167