- Test di integrazione di un database utilizzando MariaDB per sostituire MySql
- Implementazione dell'applicazione multilingue
- Salvataggio di file nell'applicazione e dati su di essi nel database
Tipi di test
Cos'è un test? Come dice Wiki: “ Un test o una prova è un modo per studiare i processi sottostanti di un sistema posizionando il sistema in diverse situazioni e monitorando i cambiamenti osservabili in esso”. In altre parole, questo è un test del corretto funzionamento del nostro sistema in determinate situazioni. Bene, vediamo quali tipologie di test esistono:-
I test unitari sono test il cui compito è testare individualmente ciascun modulo del sistema. È auspicabile che si tratti di parti minimamente divisibili del sistema, ad esempio moduli.
-
Il test di sistema è un test di alto livello per testare il funzionamento di una parte più ampia di un'applicazione o del sistema nel suo complesso.
-
Il test di regressione è un test utilizzato per verificare se nuove funzionalità o correzioni di bug influiscono sulla funzionalità esistente dell'applicazione e se ricompaiono vecchi bug.
-
Il test funzionale verifica la conformità di parte dell'applicazione con i requisiti indicati nelle specifiche, nelle storie degli utenti, ecc.
Tipi di test funzionali:
- test “white box” per la conformità di parte dell'applicazione ai requisiti con conoscenza dell'implementazione interna del sistema;
- Test “scatola nera” per la conformità di parte dell'applicazione ai requisiti senza conoscenza dell'implementazione interna del sistema.
- Il test delle prestazioni è un tipo di test scritto per determinare la velocità con cui un sistema o parte di esso funziona sotto un determinato carico.
- Test di carico - test progettati per verificare la stabilità del sistema sotto carichi standard e per trovare il picco massimo possibile al quale l'applicazione funziona correttamente.
- Lo stress test è un tipo di test progettato per verificare la funzionalità di un'applicazione sotto carichi non standard e per determinare il picco massimo possibile al quale il sistema non si bloccherà.
- Test di sicurezza : test utilizzati per verificare la sicurezza di un sistema (da attacchi di hacker, virus, accesso non autorizzato a dati riservati e altri piaceri della vita).
- Il test di localizzazione è il test di localizzazione per un'applicazione.
- Il test di usabilità è un tipo di test volto a verificare l'usabilità, la comprensibilità, l'attrattiva e l'apprendibilità per gli utenti. Tutto ciò sembra positivo, ma come funziona nella pratica? È semplice: viene utilizzata la piramide dei test di Mike Cohn: questa è una versione semplificata della piramide: ora è divisa in parti più piccole. Ma oggi non pervertiremo e considereremo l'opzione più semplice.
-
Unit : test unitari utilizzati in vari livelli dell'applicazione, testando la logica divisibile più piccola dell'applicazione: ad esempio, una classe, ma molto spesso un metodo. Questi test solitamente cercano di isolarsi il più possibile dalla logica esterna, cioè di creare l'illusione che il resto dell'applicazione funzioni in modalità standard.
Dovrebbero esserci sempre molti di questi test (più di altri tipi), poiché testano piccoli pezzi e sono molto leggeri, non consumano molte risorse (per risorse intendo RAM e tempo).
-
Integrazione : test di integrazione. Controlla parti più grandi del sistema, ovvero la combinazione di più parti logiche (diversi metodi o classi) o la correttezza di lavorare con un componente esterno. Di solito ci sono meno test di questo tipo rispetto ai test unitari, poiché sono più pesanti.
Come esempio di test di integrazione, puoi considerare la connessione a un database e la verifica che i metodi che lo utilizzano funzionino correttamente .
-
UI : test che controllano il funzionamento dell'interfaccia utente. Influenzano la logica a tutti i livelli dell'applicazione, motivo per cui sono anche chiamati end-to-end. Di norma ce ne sono molti meno, poiché sono i più pesanti e devono controllare i percorsi più necessari (utilizzati).
Nella figura sopra vediamo il rapporto tra le aree delle diverse parti del triangolo: approssimativamente la stessa proporzione viene mantenuta nel numero di queste prove nel lavoro reale.
Oggi daremo uno sguardo più da vicino ai test più utilizzati: gli unit test, poiché tutti gli sviluppatori Java che si rispettino dovrebbero essere in grado di utilizzarli a livello base.
- materiale sul Code Coverage su JavaRush e su Habré ;
- teoria fondamentale dei test .
- Stiamo scrivendo il nostro test.
- Eseguiamo il test, che sia passato o meno (vediamo che è tutto rosso, non spaventarti: dovrebbe essere così).
- Aggiungiamo il codice che dovrebbe soddisfare questo test (esegui il test).
- Rifattorizziamo il codice.
- Specificare i dati da testare (infissi).
- Utilizzando il codice sotto test (chiamando il metodo sotto test).
- Controllare i risultati e confrontarli con quelli attesi.
assertEquals(Object expecteds, Object actuals)
— controlla se gli oggetti trasmessi sono uguali.assertTrue(boolean flag)
— controlla se il valore passato restituisce true.assertFalse(boolean flag)
— controlla se il valore passato restituisce false.assertNull(Object object)
– controlla se l'oggetto è nullo.assertSame(Object firstObject, Object secondObject)
— controlla se i valori passati si riferiscono allo stesso oggetto.assertThat(T t, Matcher<T> matcher)
— controlla se t soddisfa la condizione specificata nel matcher.
Concetti chiave del test unitario
La copertura del test (Code Coverage) è una delle principali valutazioni della qualità del test dell'applicazione. Questa è la percentuale di codice coperta dai test (0-100%). In pratica, molte persone inseguono questa percentuale, cosa che non condivido, poiché iniziano ad aggiungere test dove non sono necessari. Ad esempio, il nostro servizio prevede operazioni CRUD standard (crea/ottieni/aggiorna/elimina) senza logica aggiuntiva. Questi metodi sono puramente intermediari che delegano il lavoro al livello che funziona con il repository. In questa situazione non abbiamo nulla da verificare: forse se questo metodo richiama un metodo del Tao, ma questo non è grave. Per valutare la copertura del test vengono solitamente utilizzati strumenti aggiuntivi: JaCoCo, Cobertura, Clover, Emma, ecc. Per uno studio più approfondito di questo problema, conserva un paio di articoli adatti:Fasi di test
Il test si compone di tre fasi:Ambienti di prova
Quindi ora passiamo agli affari. Sono disponibili diversi ambienti di test (framework) per Java. I più popolari sono JUnit e TestNG. Per la nostra revisione utilizziamo: Un test JUnit è un metodo contenuto in una classe utilizzato solo per il test. Una classe ha in genere lo stesso nome della classe che sta testando con +Test alla fine. Ad esempio, CarService→ CarServiceTest. Il sistema di build Maven include automaticamente tali classi nell'area di test. In effetti, questa classe è chiamata classe di test. Esaminiamo un po' le annotazioni di base: @Test - definizione di questo metodo come metodo di test (in effetti, il metodo contrassegnato con questa annotazione è uno unit test). @Before : contrassegna il metodo che verrà eseguito prima di ogni test. Ad esempio, riempiendo i dati dei test della classe, leggendo i dati di input, ecc. @After - posizionato sopra il metodo che verrà chiamato dopo ogni test (pulizia dei dati, ripristino dei valori predefiniti). @BeforeClass - posizionato sopra il metodo - analogo a @Before. Ma questo metodo viene chiamato solo una volta prima di tutti i test per una determinata classe e quindi deve essere statico. Viene utilizzato per eseguire operazioni più gravose, come il sollevamento di un database di prova. @AfterClass è l'opposto di @BeforeClass: eseguito una volta per una determinata classe, ma eseguito dopo tutti i test. Utilizzato, ad esempio, per pulire le risorse persistenti o disconnettersi dal database. @Ignore : nota che il metodo seguente è disabilitato e verrà ignorato durante l'esecuzione dei test complessivi. Viene utilizzato in diversi casi, ad esempio, se il metodo di base è stato modificato e non c'era tempo per ripetere il test. In questi casi, è consigliabile aggiungere anche una descrizione - @Ignore("Alcune descrizioni"). @Test (expected = Exception.class) - utilizzato per test negativi. Si tratta di test che controllano come si comporta un metodo in caso di errore, ovvero il test si aspetta che il metodo lanci qualche eccezione. Tale metodo è indicato dall'annotazione @Test, ma con un errore da rilevare. @Test(timeout=100) - controlla che il metodo venga eseguito in non più di 100 millisecondi. @Mock - una classe viene utilizzata su un campo per impostare un dato oggetto come mock (questo non proviene dalla libreria Junit, ma da Mockito) e, se ne abbiamo bisogno, imposteremo il comportamento del mock in una situazione specifica , direttamente nel metodo di prova. @RunWith(MockitoJUnitRunner.class) - il metodo è posizionato sopra la classe. Questo è il pulsante per eseguire i test al suo interno. I runner possono essere diversi: ad esempio ci sono i seguenti: MockitoJUnitRunner, JUnitPlatform, SpringRunner, ecc.). In JUnit 5, l'annotazione @RunWith è stata sostituita dalla più potente annotazione @ExtendWith. Diamo un'occhiata ad alcuni metodi per confrontare i risultati:assertThat(firstObject).isEqualTo(secondObject)
Qui ho parlato dei metodi di base, poiché il resto sono diverse varianti di quanto sopra.
Pratica di prova
Ora diamo un'occhiata al materiale di cui sopra utilizzando un esempio specifico. Testeremo il metodo per il servizio: aggiornamento. Non prenderemo in considerazione il livello dao, poiché è il nostro livello predefinito. Aggiungiamo uno starter per i test:<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.2.2.RELEASE</version>
<scope>test</scope>
</dependency>
Quindi, la classe di servizio:
@Service
@RequiredArgsConstructor
public class RobotServiceImpl implements RobotService {
private final RobotDAO robotDAO;
@Override
public Robot update(Long id, Robot robot) {
Robot found = robotDAO.findById(id);
return robotDAO.update(Robot.builder()
.id(id)
.name(robot.getName() != null ? robot.getName() : found.getName())
.cpu(robot.getCpu() != null ? robot.getCpu() : found.getCpu())
.producer(robot.getProducer() != null ? robot.getProducer() : found.getProducer())
.build());
}
}
8 - estrai l'oggetto aggiornato dal database 9-14 - crea l'oggetto tramite il builder, se l'oggetto in arrivo ha un campo - impostalo, in caso contrario - lascia ciò che è nel database E guarda il nostro test:
@RunWith(MockitoJUnitRunner.class)
public class RobotServiceImplTest {
@Mock
private RobotDAO robotDAO;
private RobotServiceImpl robotService;
private static Robot testRobot;
@BeforeClass
public static void prepareTestData() {
testRobot = Robot
.builder()
.id(123L)
.name("testRobotMolly")
.cpu("Intel Core i7-9700K")
.producer("China")
.build();
}
@Before
public void init() {
robotService = new RobotServiceImpl(robotDAO);
}
1 — il nostro Runner 4 — isola il servizio dal livello dao sostituendolo con un mock 11 — imposta un'entità di test per la classe (quella che useremo come criceto di prova) 22 — imposta un oggetto di servizio che testeremo
@Test
public void updateTest() {
when(robotDAO.findById(any(Long.class))).thenReturn(testRobot);
when(robotDAO.update(any(Robot.class))).then(returnsFirstArg());
Robot robotForUpdate = Robot
.builder()
.name("Vally")
.cpu("AMD Ryzen 7 2700X")
.build();
Robot resultRobot = robotService.update(123L, robotForUpdate);
assertNotNull(resultRobot);
assertSame(resultRobot.getId(),testRobot.getId());
assertThat(resultRobot.getName()).isEqualTo(robotForUpdate.getName());
assertTrue(resultRobot.getCpu().equals(robotForUpdate.getCpu()));
assertEquals(resultRobot.getProducer(),testRobot.getProducer());
}
Qui vediamo una chiara divisione del test in tre parti: 3-9 - impostazione dei dispositivi 11 - esecuzione della parte testata 13-17 - verifica dei risultati Maggiori dettagli: 3-4 - impostazione del comportamento per moka dao 5 - impostazione di un'istanza che aggiorneremo in aggiunta al nostro standard 11 - usa il metodo e prendi l'istanza risultante 13 - controlla che non sia zero 14 - controlla l'ID del risultato e gli argomenti del metodo specificati 15 - controlla se il nome è stato aggiornato 16 - guarda al risultato dalla CPU 17 - poiché non l'abbiamo impostato nel campo dell'istanza di aggiornamento, dovrebbe rimanere lo stesso, controlliamolo. Lanciamo: il test è verde, puoi espirare)) Quindi, riassumiamo: il test migliora la qualità del codice e rende il processo di sviluppo più flessibile e affidabile. Immagina quanti sforzi dovremmo spendere per riprogettare il software con centinaia di file di classe. Una volta scritti i test unitari per tutte queste classi, possiamo effettuare il refactoring con sicurezza. E, cosa più importante, ci aiuta a trovare facilmente gli errori durante lo sviluppo. Ragazzi, per me oggi è tutto: mettete mi piace, scrivete commenti)))
GO TO FULL VERSION