Étude de cas
Objectifs du chapitre :
- Explorer des études de cas réels d'application de JUnit dans des projets logiciels.
- Comprendre comment JUnit peut être intégré dans différents contextes de développement.
- Apprendre des exemples concrets des meilleures pratiques et des défis rencontrés lors de l'utilisation de JUnit.
18.1 Étude de cas 1 : Application Web de Gestion des Utilisateurs
Contexte : Une entreprise développe une application web de gestion des utilisateurs. L'application permet de créer, lire, mettre à jour et supprimer des utilisateurs (opérations CRUD). Le projet utilise Spring Boot comme framework principal.
Objectifs :
- Assurer la qualité du code en testant les fonctionnalités CRUD.
- Automatiser les tests pour une intégration continue fluide.
Approche :
- Configuration du projet :
- Utilisation de Maven pour gérer les dépendances.
- Inclusion de JUnit et Mockito pour les tests unitaires et de mock.
Dépendances Maven :
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.11.2</version>
<scope>test</scope>
</dependency>
</dependencies>
- Création des tests unitaires :
- Écriture des tests pour les services de gestion des utilisateurs en utilisant JUnit et Mockito.
Exemple de test unitaire pour le service utilisateur (UserServiceTest.java) :
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
public class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@BeforeEach
public void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
public void testCreateUser() {
User user = new User("john.doe", "John", "Doe");
when(userRepository.save(user)).thenReturn(user);
User createdUser = userService.createUser(user);
assertNotNull(createdUser);
assertEquals("john.doe", createdUser.getUsername());
verify(userRepository, times(1)).save(user);
}
}
- Exécution des tests dans Jenkins :
- Configuration d'un job Jenkins pour exécuter les tests à chaque commit.
Configuration du job Jenkins :
- Utilisation du plugin Maven pour exécuter les commandes
mvn clean test. - Ajout de l'étape
Publish JUnit test result reportpour afficher les résultats des tests.
18.2 Étude de cas 2 : Application Mobile de Gestion de Budget
Contexte : Une startup développe une application mobile de gestion de budget. L'application permet aux utilisateurs de suivre leurs dépenses et revenus, de créer des budgets et d'analyser leurs finances. Le projet utilise Android et Kotlin.
Objectifs :
- Garantir la fiabilité des fonctionnalités critiques de l'application.
- Mettre en place une infrastructure de tests automatisés.
Approche :
- Configuration du projet :
- Utilisation de Gradle pour gérer les dépendances.
- Inclusion de JUnit et Espresso pour les tests unitaires et d'interface utilisateur.
Dépendances Gradle :
dependencies {
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
- Création des tests unitaires et UI :
- Écriture des tests unitaires pour les composants logiques de l'application.
- Écriture des tests d'interface utilisateur pour les interactions critiques.
Exemple de test unitaire pour le service budget (BudgetServiceTest.kt) :
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.MockitoAnnotations
class BudgetServiceTest {
@Mock
private lateinit var budgetRepository: BudgetRepository
private lateinit var budgetService: BudgetService
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
budgetService = BudgetService(budgetRepository)
}
@Test
fun testCalculateTotalExpenses() {
val expenses = listOf(Expense(100.0), Expense(50.0))
Mockito.`when`(budgetRepository.getExpenses()).thenReturn(expenses)
val totalExpenses = budgetService.calculateTotalExpenses()
assertEquals(150.0, totalExpenses, 0.0)
}
}
Exemple de test UI pour l'écran de budget (BudgetActivityTest.kt) :
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.rule.ActivityTestRule
import androidx.test.ext.junit.rules.activityScenarioRule
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class BudgetActivityTest {
@get:Rule
var activityScenarioRule = activityScenarioRule<BudgetActivity>()
@Test
fun testAddExpenseButton() {
onView(withId(R.id.add_expense_button)).perform(click())
onView(withId(R.id.expense_form)).check(matches(isDisplayed()))
}
}
- Exécution des tests dans Jenkins :
- Configuration d'un job Jenkins pour exécuter les tests unitaires et UI à chaque commit.
Configuration du job Jenkins :
- Utilisation du plugin Gradle pour exécuter les commandes
gradle clean testetgradle connectedAndroidTest. - Ajout de l'étape
Publish JUnit test result reportpour afficher les résultats des tests unitaires. - Utilisation du plugin Android Emulator pour exécuter les tests UI sur un émulateur Android.
18.3 Étude de cas 3 : Système de Commerce Électronique
Contexte : Une entreprise développe un système de commerce électronique pour vendre des produits en ligne. Le système comprend des fonctionnalités comme la gestion des produits, le panier d'achat et le paiement. Le projet utilise Spring Boot et Angular.
Objectifs :
- Assurer la qualité et la performance des fonctionnalités critiques du système.
- Automatiser les tests pour une intégration et une livraison continues.
Approche :
- Configuration du projet :
- Utilisation de Maven pour gérer les dépendances backend et npm pour les dépendances frontend.
- Inclusion de JUnit et Mockito pour les tests backend et Jasmine/Karma pour les tests frontend.
Dépendances Maven (backend) :
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.11.2</version>
<scope>test</scope>
</dependency>
</dependencies>
Dépendances npm (frontend) :
{
"devDependencies": {
"karma": "~6.3.4",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~4.0.1",
"karma-jasmine-html-reporter": "^1.5.0",
"jasmine-core": "~3.7.1",
"jasmine-spec-reporter": "~5.0.0"
}
}
- Création des tests unitaires et end-to-end (E2E) :
- Écriture des tests unitaires pour les services backend.
- Écriture des tests E2E pour les fonctionnalités frontend.
Exemple de test unitaire pour le service produit (ProductServiceTest.java) :
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
public class ProductServiceTest {
@Mock
private ProductRepository productRepository;
@InjectMocks
private ProductService productService;
@BeforeEach
public void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
public void testFindProductById() {
Product product = new Product(1L, "Laptop", 1000.0);
when(productRepository.findById(1L)).thenReturn(Optional.of(product));
Product foundProduct = productService.findProductById(1L);
assertNotNull(foundProduct);
assertEquals("Laptop", foundProduct.getName());
verify(productRepository, times(1)).findById(1L);
}
}
Exemple de test E2E pour l'ajout au panier (add-to-cart.e2e-spec.ts) :
import { browser, by, element } from 'protractor';
describe('Add to Cart', () => {
it('should add a product to the cart', async () => {
await browser.get('/products');
await element(by.css('.product-add-to-cart')).click();
const cartItems = await element.all(by.css('.cart-item'));
expect(cartItems.length).toBe(1);
});
});
- Exécution des tests dans Jenkins :
- Configuration d'un job Jenkins pour exécuter les tests backend et frontend à chaque commit.
Configuration du job Jenkins :
- Utilisation du plugin Maven pour exécuter les commandes
mvn clean test. - Utilisation de npm pour exécuter les commandes
npm testpour les tests unitaires etng e2epour les tests E2E. - Ajout de l'étape
Publish JUnit test result reportpour afficher les résultats des tests backend.
18.4 Bonnes pratiques tirées des études de cas
- Intégration continue :
- Configurer des pipelines CI/CD pour automatiser l'exécution des tests et des builds à chaque commit.
- Tests unitaires et d'intégration :
- Écrire des tests unitaires pour les composants individuels et des tests d'intégration pour vérifier l'interaction entre les composants.
- Tests d'interface utilisateur :
- Inclure des tests d'interface utilisateur pour valider les interactions critiques.
- Mocks et Stubs :
- Utiliser Mockito pour créer des mocks et stubs afin d'isoler les composants testés.
- Analyse des rapports de test :
- Analyser régulièrement les rapports de test pour identifier et corriger rapidement les problèmes.
Résumé du chapitre :
- Les études de cas montrent comment JUnit peut être intégré dans différents contextes de développement pour assurer la qualité du code.
- L'intégration continue avec Jenkins facilite l'automatisation des tests et des builds.
- Les bonnes pratiques incluent l'écriture de tests unitaires, d'intégration, et d'interface utilisateur, ainsi que l'utilisation de mocks et stubs pour isoler les composants testés.