Test di applicazioni thread

In questo capitolo impareremo a testare le applicazioni thread. Impareremo anche l'importanza dei test.

Perché testare?

Prima di immergerci nella discussione sull'importanza del test, dobbiamo sapere cosa sta testando. In termini generali, il test è una tecnica per scoprire quanto bene sta funzionando qualcosa. D'altra parte, in particolare se parliamo di programmi per computer o software, il test è la tecnica per accedere alla funzionalità di un programma software.

In questa sezione, discuteremo l'importanza del test del software. Nello sviluppo del software, deve essere effettuato un doppio controllo prima del rilascio del software al client. Ecco perché è molto importante testare il software da un team di test esperto. Considera i seguenti punti per comprendere l'importanza del test del software:

Miglioramento della qualità del software

Certamente, nessuna azienda vuole fornire software di bassa qualità e nessun cliente vuole acquistare software di bassa qualità. I test migliorano la qualità del software trovando e correggendo i bug in esso.

Soddisfazione dei clienti

La parte più importante di qualsiasi attività commerciale è la soddisfazione dei propri clienti. Fornendo software privo di bug e di buona qualità, le aziende possono raggiungere la soddisfazione del cliente.

Riduci l'impatto delle nuove funzionalità

Supponiamo di aver realizzato un sistema software di 10000 linee e di dover aggiungere una nuova funzionalità, quindi il team di sviluppo si preoccuperebbe dell'impatto di questa nuova funzionalità sull'intero software. Anche qui i test giocano un ruolo fondamentale perché se il team di test ha realizzato una buona suite di test, può salvarci da potenziali interruzioni catastrofiche.

L'esperienza utente

Un'altra parte più importante di qualsiasi attività commerciale è l'esperienza degli utenti di quel prodotto. Solo i test possono garantire che l'utente finale trovi semplice e facile usare il prodotto.

Abbattere le spese

Il test può ridurre il costo totale del software trovando e correggendo i bug nella fase di test del suo sviluppo piuttosto che risolverli dopo la consegna. Se si verifica un bug importante dopo la consegna del software, aumenterebbe il suo costo tangibile, ad esempio in termini di spese e il costo intangibile, ad esempio in termini di insoddisfazione del cliente, reputazione negativa dell'azienda, ecc.

Cosa testare?

È sempre consigliabile avere una conoscenza adeguata di ciò che deve essere testato. In questa sezione, capiremo innanzitutto che è il motivo principale del tester durante il test di qualsiasi software. La copertura del codice, ovvero il numero di righe di codice che la nostra suite di test raggiunge durante il test, dovrebbe essere evitato. È perché, durante il test, concentrarsi solo sul numero di righe di codice non aggiunge alcun valore reale al nostro sistema. Potrebbero rimanere alcuni bug, che si riflettono in una fase successiva anche dopo la distribuzione.

Considera i seguenti punti importanti relativi a cosa testare:

  • Dobbiamo concentrarci sul test della funzionalità del codice piuttosto che sulla copertura del codice.

  • Dobbiamo prima testare le parti più importanti del codice e poi spostarci verso le parti meno importanti del codice. Risparmierà sicuramente tempo.

  • Il tester deve avere una moltitudine di test diversi che possono spingere il software fino ai suoi limiti.

Approcci per testare programmi software concorrenti

Grazie alla capacità di utilizzare la vera capacità dell'architettura multi-core, i sistemi software simultanei stanno sostituendo i sistemi sequenziali. In tempi recenti, i programmi di sistema simultanei vengono utilizzati in tutto, dai telefoni cellulari alle lavatrici, dalle automobili agli aeroplani, ecc. Dobbiamo essere più attenti nel testare i programmi software concorrenti perché se abbiamo aggiunto più thread a un'applicazione a thread singolo che già un bug, quindi ci ritroveremmo con più bug.

Le tecniche di test per programmi software simultanei si concentrano ampiamente sulla selezione di interleaving che espongono schemi potenzialmente dannosi come condizioni di competizione, deadlock e violazione dell'atomicità. Di seguito sono riportati due approcci per testare programmi software simultanei:

Esplorazione sistematica

Questo approccio mira a esplorare lo spazio degli intrecci nel modo più ampio possibile. Tali approcci possono adottare una tecnica di forza bruta e altri adottano una tecnica di riduzione dell'ordine parziale o una tecnica euristica per esplorare lo spazio degli intrecci.

Guidato dalla proprietà

Gli approcci basati sulla proprietà si basano sull'osservazione che gli errori di concorrenza hanno maggiori probabilità di verificarsi in interlivelli che espongono proprietà specifiche come il modello di accesso alla memoria sospetto. Diversi approcci basati sulla proprietà prendono di mira diversi errori come condizioni di gara, deadlock e violazione dell'atomicità, che dipendono ulteriormente da una o da altre proprietà specifiche.

Strategie di test

La strategia di test è anche nota come approccio di test. La strategia definisce le modalità di esecuzione dei test. L'approccio al test ha due tecniche:

Proattivi

Un approccio in cui il processo di progettazione del test viene avviato il prima possibile al fine di trovare e correggere i difetti prima della creazione della build.

Reattivo

Un approccio in cui il test non inizia fino al completamento del processo di sviluppo.

Prima di applicare qualsiasi strategia o approccio di test al programma python, dobbiamo avere un'idea di base sul tipo di errori che un programma software può avere. Gli errori sono i seguenti:

Errori sintattici

Durante lo sviluppo del programma possono verificarsi molti piccoli errori. Gli errori sono principalmente dovuti a errori di battitura. Ad esempio, due punti mancanti o un'ortografia errata di una parola chiave, ecc. Tali errori sono dovuti a un errore nella sintassi del programma e non nella logica. Quindi, questi errori sono chiamati errori sintattici.

Errori semantici

Gli errori semantici sono anche chiamati errori logici. Se c'è un errore logico o semantico nel programma software, l'istruzione verrà compilata ed eseguita correttamente ma non darà l'output desiderato perché la logica non è corretta.

Test unitario

Questa è una delle strategie di test più utilizzate per testare i programmi Python. Questa strategia viene utilizzata per testare unità o componenti del codice. Per unità o componenti si intendono classi o funzioni del codice. Il test unitario semplifica il test di sistemi di programmazione di grandi dimensioni testando unità "piccole". Con l'aiuto del concetto di cui sopra, il test unitario può essere definito come un metodo in cui vengono testate singole unità di codice sorgente per determinare se restituiscono l'output desiderato.

Nelle sezioni successive, impareremo a conoscere i diversi moduli Python per i test unitari.

modulo unittest

Il primo modulo per i test unitari è il modulo unittest. Si ispira a JUnit e per impostazione predefinita è incluso in Python3.6. Supporta l'automazione dei test, la condivisione del codice di configurazione e spegnimento per i test, l'aggregazione dei test in raccolte e l'indipendenza dei test dal framework di reporting.

Di seguito sono riportati alcuni concetti importanti supportati dal modulo unittest

Dispositivo di testo

Viene utilizzato per impostare un test in modo che possa essere eseguito prima di iniziare il test e smontato dopo la fine del test. Può comportare la creazione di database temporanei, directory, ecc. Necessari prima di iniziare il test.

Scenario di prova

Lo scenario di test verifica se una risposta richiesta proviene o meno dal set specifico di input. Il modulo unittest include una classe base denominata TestCase che può essere utilizzata per creare nuovi casi di test. Include due metodi predefiniti:

  • setUp()- un metodo a gancio per impostare l'attrezzatura di prova prima di esercitarla. Viene chiamato prima di chiamare i metodi di test implementati.

  • tearDown( - un metodo hook per decostruire l'apparecchiatura di classe dopo aver eseguito tutti i test nella classe.

Suite di test

È una raccolta di suite di test, casi di test o entrambi.

Test runner

Controlla l'esecuzione dei casi di test o delle tute e fornisce il risultato all'utente. Può utilizzare una GUI o una semplice interfaccia di testo per fornire il risultato.

Example

Il seguente programma Python utilizza il modulo unittest per testare un modulo denominato Fibonacci. Il programma aiuta a calcolare la serie di un numero di Fibonacci. In questo esempio, abbiamo creato una classe denominata Fibo_test, per definire i casi di test utilizzando metodi diversi. Questi metodi vengono ereditati da unittest.TestCase. Usiamo due metodi predefiniti: setUp () e tearDown (). Definiamo anche il metodo testfibocale. Il nome del test deve essere iniziato con la lettera test. Nel blocco finale, unittest.main () fornisce un'interfaccia della riga di comando allo script di test.

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 viene eseguito dalla riga di comando, lo script sopra produce un output simile a questo:

Produzione

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

Ora, per renderlo più chiaro, stiamo cambiando il nostro codice che ha aiutato a definire il modulo Fibonacci.

Considera il seguente blocco di codice come esempio:

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

Alcune modifiche al blocco di codice vengono apportate come mostrato di seguito:

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

Ora, dopo aver eseguito lo script con il codice modificato, otterremo il seguente output:

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)

L'output sopra mostra che il modulo non è riuscito a fornire l'output desiderato.

Modulo Docktest

Il modulo docktest aiuta anche nei test unitari. Inoltre è preconfezionato con python. È più facile da usare rispetto al modulo unittest. Il modulo unittest è più adatto per test complessi. Per utilizzare il modulo doctest, dobbiamo importarlo. La docstring della funzione corrispondente deve avere una sessione Python interattiva insieme ai loro output.

Se tutto va bene nel nostro codice, non ci sarà alcun output dal modulo docktest; in caso contrario, fornirà l'output.

Esempio

Il seguente esempio Python usa il modulo docktest per testare un modulo chiamato Fibonacci, che aiuta a calcolare la serie di Fibonacci di un numero.

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()

Possiamo vedere che la docstring della funzione corrispondente denominata fib aveva una sessione Python interattiva insieme agli output. Se il nostro codice va bene, non ci sarebbe alcun output dal modulo doctest. Ma per vedere come funziona possiamo eseguirlo con l'opzione –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.

Ora cambieremo il codice che ha aiutato a definire il modulo Fibonacci

Considera il seguente blocco di codice come esempio:

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

Il seguente blocco di codice aiuta con le modifiche:

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

Dopo aver eseguito lo script anche senza l'opzione –v, con il codice modificato, otterremo l'output come mostrato di seguito.

Produzione

(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.

Possiamo vedere nell'output sopra che tre test hanno fallito.