Testando aplicativos de thread

Neste capítulo, aprenderemos sobre o teste de aplicativos de thread. Também aprenderemos a importância do teste.

Por que testar?

Antes de mergulharmos na discussão sobre a importância do teste, precisamos saber o que é teste. Em termos gerais, o teste é uma técnica para descobrir se algo está funcionando bem. Por outro lado, especificamente se falamos de programas de computador ou software, o teste é a técnica de acessar a funcionalidade de um programa de software.

Nesta seção, discutiremos a importância do teste de software. No desenvolvimento de software, deve haver uma verificação dupla antes da liberação do software para o cliente. É por isso que é muito importante testar o software por uma equipe de teste experiente. Considere os seguintes pontos para entender a importância do teste de software -

Melhoria da qualidade do software

Certamente, nenhuma empresa deseja fornecer software de baixa qualidade e nenhum cliente deseja comprar software de baixa qualidade. O teste melhora a qualidade do software, encontrando e corrigindo os bugs nele.

Satisfação dos clientes

A parte mais importante de qualquer negócio é a satisfação de seus clientes. Ao fornecer software de boa qualidade e sem bugs, as empresas podem alcançar a satisfação do cliente.

Reduza o impacto de novos recursos

Suponha que fizemos um sistema de software de 10.000 linhas e precisamos adicionar um novo recurso, então a equipe de desenvolvimento se preocuparia com o impacto desse novo recurso em todo o software. Aqui, também, o teste desempenha um papel vital porque se a equipe de teste fez um bom conjunto de testes, ele pode nos salvar de quaisquer interrupções catastróficas em potencial.

Experiência de usuário

Outra parte mais importante de qualquer negócio é a experiência dos usuários desse produto. Somente o teste pode garantir que o usuário final ache simples e fácil de usar o produto.

Reduzindo despesas

O teste pode reduzir o custo total do software, encontrando e corrigindo os bugs na fase de teste de seu desenvolvimento, em vez de corrigi-lo após a entrega. Se houver um grande bug após a entrega do software, isso aumentaria seu custo tangível, digamos em termos de despesas e custo intangível, digamos, em termos de insatisfação do cliente, reputação negativa da empresa etc.

O que testar?

É sempre recomendável ter o conhecimento adequado do que deve ser testado. Nesta seção, primeiro entenderemos ser o motivo principal do testador ao testar qualquer software. A cobertura de código, ou seja, quantas linhas de código nosso conjunto de testes atinge, durante o teste, deve ser evitada. É porque, durante o teste, focar apenas no número de linhas de códigos não adiciona nenhum valor real ao nosso sistema. Podem permanecer alguns bugs, que refletem posteriormente em um estágio posterior, mesmo após a implantação.

Considere os seguintes pontos importantes relacionados ao que testar -

  • Precisamos nos concentrar em testar a funcionalidade do código em vez da cobertura do código.

  • Precisamos testar as partes mais importantes do código primeiro e, em seguida, passar para as partes menos importantes do código. Isso definitivamente vai economizar tempo.

  • O testador deve ter vários testes diferentes que podem levar o software até seus limites.

Abordagens para testar programas de software simultâneos

Devido à capacidade de utilizar a verdadeira capacidade da arquitetura multi-core, os sistemas de software simultâneos estão substituindo os sistemas sequenciais. Nos últimos tempos, programas de sistema simultâneos estão sendo usados ​​em tudo, de telefones celulares a máquinas de lavar, de carros a aviões, etc. Precisamos ser mais cuidadosos ao testar os programas de software simultâneos, porque se tivermos adicionado vários threads a um aplicativo de thread único, já é um bug, então acabaríamos com vários bugs.

As técnicas de teste para programas de software simultâneos concentram-se extensivamente na seleção de intercalações que expõem padrões potencialmente prejudiciais, como condições de corrida, impasses e violação de atomicidade. A seguir estão duas abordagens para testar programas de software simultâneos -

Exploração sistemática

Essa abordagem visa explorar o espaço das intercalações da forma mais ampla possível. Essas abordagens podem adotar uma técnica de força bruta e outras adotam uma técnica de redução de ordem parcial ou técnica heurística para explorar o espaço de intercalações.

Orientado à propriedade

Abordagens baseadas em propriedade contam com a observação de que as falhas de simultaneidade são mais prováveis ​​de ocorrer em intercalações que expõem propriedades específicas, como padrão de acesso à memória suspeito. Diferentes abordagens orientadas por propriedades visam falhas diferentes, como condições de corrida, impasses e violação de atomicidade, que depende ainda de uma ou outras propriedades específicas.

Estratégias de teste

Estratégia de teste também é conhecida como abordagem de teste. A estratégia define como o teste será realizado. A abordagem de teste tem duas técnicas -

Proativo

Uma abordagem em que o processo de design de teste é iniciado o mais cedo possível para localizar e corrigir os defeitos antes da criação do build.

Reativo

Uma abordagem em que o teste não começa até a conclusão do processo de desenvolvimento.

Antes de aplicar qualquer estratégia ou abordagem de teste no programa python, devemos ter uma ideia básica sobre os tipos de erros que um programa de software pode ter. Os erros são os seguintes -

Erros sintáticos

Durante o desenvolvimento do programa, pode haver muitos pequenos erros. Os erros são principalmente devido a erros de digitação. Por exemplo, falta de dois pontos ou grafia incorreta de uma palavra-chave, etc. Esses erros se devem a um erro na sintaxe do programa e não na lógica. Conseqüentemente, esses erros são chamados de erros sintáticos.

Erros semânticos

Os erros semânticos também são chamados de erros lógicos. Se houver um erro lógico ou semântico no programa de software, a instrução será compilada e executada corretamente, mas não dará a saída desejada porque a lógica não está correta.

Teste de Unidade

Esta é uma das estratégias de teste mais usadas para testar programas Python. Essa estratégia é usada para testar unidades ou componentes do código. Por unidades ou componentes, queremos dizer classes ou funções do código. O teste de unidade simplifica o teste de grandes sistemas de programação testando unidades “pequenas”. Com a ajuda do conceito acima, o teste de unidade pode ser definido como um método em que unidades individuais de código-fonte são testadas para determinar se retornam a saída desejada.

Em nossas seções subsequentes, aprenderemos sobre os diferentes módulos Python para teste de unidade.

módulo de teste de unidade

O primeiro módulo para teste de unidade é o módulo de teste de unidade. Ele é inspirado no JUnit e, por padrão, incluído no Python3.6. Ele suporta automação de teste, compartilhamento de configuração e código de desligamento para testes, agregação de testes em coleções e independência dos testes da estrutura de relatório.

A seguir estão alguns conceitos importantes suportados pelo módulo unittest

Fixação de texto

É usado para configurar um teste para que possa ser executado antes de iniciar o teste e desmontado após o término do teste. Pode envolver a criação de banco de dados temporário, diretórios, etc. necessários antes de iniciar o teste.

Caso de teste

O caso de teste verifica se uma resposta necessária está vindo de um conjunto específico de entradas ou não. O módulo unittest inclui uma classe base chamada TestCase que pode ser usada para criar novos casos de teste. Inclui dois métodos padrão -

  • setUp()- um método de gancho para configurar o dispositivo de teste antes de exercitá-lo. Isso é chamado antes de chamar os métodos de teste implementados.

  • tearDown( - um método de gancho para desconstruir o fixture da classe depois de executar todos os testes da classe.

Suíte de teste

É uma coleção de suítes de teste, casos de teste ou ambos.

Corredor de teste

Ele controla a execução dos casos de teste ou trajes e fornece o resultado ao usuário. Ele pode usar GUI ou interface de texto simples para fornecer o resultado.

Example

O seguinte programa Python usa o módulo unittest para testar um módulo chamado Fibonacci. O programa ajuda a calcular a série de Fibonacci de um número. Neste exemplo, criamos uma classe chamada Fibo_test, para definir os casos de teste usando métodos diferentes. Esses métodos são herdados de unittest.TestCase. Estamos usando dois métodos padrão - setUp () e tearDown (). Também definimos o método testfibocal. O nome do teste deve ser iniciado com a letra teste. No bloco final, unittest.main () fornece uma interface de linha de comando para o script de teste.

import unittest
def fibonacci(n):
   a, b = 0, 1
   for i in range(n):
   a, b = b, a + b
   return a
class Fibo_Test(unittest.TestCase):
   def setUp(self):
   print("This is run before our tests would be executed")
   def tearDown(self):
   print("This is run after the completion of execution of our tests")

   def testfibocal(self):
   self.assertEqual(fib(0), 0)
   self.assertEqual(fib(1), 1)
   self.assertEqual(fib(5), 5)
   self.assertEqual(fib(10), 55)
   self.assertEqual(fib(20), 6765)

if __name__ == "__main__":
   unittest.main()

Quando executado a partir da linha de comando, o script acima produz uma saída semelhante a esta -

Resultado

This runs before our tests would be executed.
This runs after the completion of execution of our tests.
.
----------------------------------------------------------------------
Ran 1 test in 0.006s
OK

Agora, para deixar mais claro, estamos mudando nosso código que ajudou na definição do módulo Fibonacci.

Considere o seguinte bloco de código como exemplo -

def fibonacci(n):
   a, b = 0, 1
   for i in range(n):
   a, b = b, a + b
   return a

Algumas mudanças no bloco de código são feitas conforme mostrado abaixo -

def fibonacci(n):
   a, b = 1, 1
   for i in range(n):
   a, b = b, a + b
   return a

Agora, depois de executar o script com o código alterado, obteremos a seguinte saída -

This runs before our tests would be executed.
This runs after the completion of execution of our tests.
F
======================================================================
FAIL: testCalculation (__main__.Fibo_Test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "unitg.py", line 15, in testCalculation
self.assertEqual(fib(0), 0)
AssertionError: 1 != 0
----------------------------------------------------------------------
Ran 1 test in 0.007s

FAILED (failures = 1)

A saída acima mostra que o módulo falhou em fornecer a saída desejada.

Módulo Docktest

O módulo docktest também ajuda no teste de unidade. Ele também vem pré-empacotado com python. É mais fácil de usar do que o módulo unittest. O módulo unittest é mais adequado para testes complexos. Para usar o módulo doctest, precisamos importá-lo. A docstring da função correspondente deve ter uma sessão Python interativa junto com suas saídas.

Se tudo estiver bem em nosso código, não haverá saída do módulo docktest; caso contrário, ele fornecerá a saída.

Exemplo

O exemplo Python a seguir usa o módulo docktest para testar um módulo chamado Fibonacci, que ajuda a calcular a série Fibonacci de um número.

import doctest
def fibonacci(n):
   """
   Calculates the Fibonacci number

   >>> fibonacci(0)
   0
   >>> fibonacci(1)
   1
   >>> fibonacci(10)
   55
   >>> fibonacci(20)
   6765
   >>>

   """
   a, b = 1, 1
   for i in range(n):
   a, b = b, a + b
   return a
      if __name__ == "__main__":
   doctest.testmod()

Podemos ver que a docstring da função correspondente chamada fib teve uma sessão Python interativa junto com as saídas. Se nosso código estiver correto, não haverá saída do módulo doctest. Mas para ver como funciona, podemos executá-lo com a opção –v.

(base) D:\ProgramData>python dock_test.py -v
Trying:
   fibonacci(0)
Expecting:
   0
ok
Trying:
   fibonacci(1)
Expecting:
   1
ok
Trying:
   fibonacci(10)
Expecting:
   55
ok
Trying:
   fibonacci(20)
Expecting:
   6765
ok
1 items had no tests:
   __main__
1 items passed all tests:
4 tests in __main__.fibonacci
4 tests in 2 items.
4 passed and 0 failed.
Test passed.

Agora, vamos mudar o código que ajudou na definição do módulo Fibonacci

Considere o seguinte bloco de código como exemplo -

def fibonacci(n):
   a, b = 0, 1
   for i in range(n):
   a, b = b, a + b
   return a

O seguinte bloco de código ajuda com as mudanças -

def fibonacci(n):
   a, b = 1, 1
   for i in range(n):
   a, b = b, a + b
   return a

Depois de executar o script mesmo sem a opção –v, com o código alterado, obteremos a saída conforme mostrado abaixo.

Resultado

(base) D:\ProgramData>python dock_test.py
**********************************************************************
File "unitg.py", line 6, in __main__.fibonacci
Failed example:
   fibonacci(0)
Expected:
   0
Got:
   1
**********************************************************************
File "unitg.py", line 10, in __main__.fibonacci
Failed example:
   fibonacci(10)
Expected:
   55
Got:
   89
**********************************************************************
File "unitg.py", line 12, in __main__.fibonacci
Failed example:
   fibonacci(20)
Expected:
   6765
Got:
   10946
**********************************************************************
1 items had failures:
   3 of 4 in __main__.fibonacci
***Test Failed*** 3 failures.

Podemos ver na saída acima que três testes falharam.