🧪 Tests

testing-mock-designer

Conception de mocks, stubs et fakes pour les tests unitaires et d'intégration avec les principaux frameworks de mocking.

⚡ Installation & lancement en 1 commande

Copiez-collez dans votre terminal : le skill s'installe dans ~/.claude/skills et Claude Code se lance directement dessus.

macOS / Linux
curl -fsSL https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.sh | sh -s -- testing-mock-designer --launch
Windows (PowerShell)
iex "& { $(iwr -useb https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.ps1) } testing-mock-designer -Launch"

🚀 Déjà installé ?

claude "/testing-mock-designer"

Ou tapez /testing-mock-designer dans une session Claude Code, ou décrivez simplement votre besoin — le skill se déclenche automatiquement via le skill-router.

🔑 Déclencheurs automatiques

Le skill s'active automatiquement quand votre demande contient :

mockstubfakeMoqNSubstituteMockitojest.mock

📦 Installation manuelle

git clone https://github.com/khalilbenaz/claude-skills-collection.git cp -r claude-skills-collection/skills/testing-mock-designer ~/.claude/skills/

Payload du plugin : skills/testing-mock-designer · source éditable : testing-skills/mock-designer

📖 Manuel

Mock Designer

Critères de décision — quel test double choisir ?

BesoinTypeExemple
Fournir une valeur de retour fixeStubrepo.GetById() retourne un objet préconfiguré
Vérifier qu'un appel a eu lieuMockemailSender.Send() appelé exactement 1 fois
Remplacer une dépendance lourdeFakeInMemoryRepository à la place d'une vraie BDD
Capturer les arguments d'un appelSpyLogger — lire le message qui a été loggué
Remplacer une fonction globaleMonkey patchDate.now, Math.random en JS

Règle simple : stub si tu consommes, mock si tu produis un effet observable.


Workflow en étapes

1. Identifier les dépendances à simuler

Code sous test : OrderService(IOrderRepository, IPaymentGateway, IClock)
→ mocker : IOrderRepository, IPaymentGateway, IClock
→ NE PAS mocker : Money, OrderId, OrderStatus

2. Choisir le framework et configurer

.NET — Moq

// Installer : dotnet add package Moq
var repo = new Mock<IOrderRepository>();
repo.Setup(r => r.GetById(42)).ReturnsAsync(new Order { Id = 42 });

.NET — NSubstitute (plus lisible, préféré en Arrange/Act/Assert pur)

// Installer : dotnet add package NSubstitute
var repo = Substitute.For<IOrderRepository>();
repo.GetById(42).Returns(new Order { Id = 42 });

Java — Mockito

// Maven : org.mockito:mockito-core
OrderRepository repo = mock(OrderRepository.class);
when(repo.findById(42L)).thenReturn(Optional.of(new Order(42L)));

JavaScript/TypeScript — Jest

jest.mock('../services/emailService');
const emailService = emailService as jest.Mocked<typeof emailService>;
emailService.send.mockResolvedValue({ messageId: 'abc' });

Python — unittest.mock

from unittest.mock import MagicMock, patch

repo = MagicMock()
repo.get_by_id.return_value = Order(id=42)

3. Configurer les stubs (données)

Couvrir systématiquement trois scénarios :

// Succès nominal
repo.Setup(r => r.GetById(1)).ReturnsAsync(ValidOrder());

// Cas limite (entité inexistante)
repo.Setup(r => r.GetById(0)).ReturnsAsync((Order?)null);

// Erreur infrastructure
repo.Setup(r => r.GetById(99)).ThrowsAsync(new DbException("timeout"));

Utiliser un Object Mother pour factoriser les données de test :

public static class OrderMother
{
    public static Order Valid() => new Order { Id = 1, Amount = 100m, Status = OrderStatus.Pending };
    public static Order Expired() => Valid() with { CreatedAt = DateTime.UtcNow.AddDays(-31) };
}

4. Implémenter les mocks avec vérifications d'interaction

Ne vérifier les interactions que si l'interaction est le comportement à tester (effet de bord observable).

// Moq — vérifier qu'un email a été envoyé exactement une fois
emailSender.Verify(e => e.Send(It.Is<Email>(m => m.To == "user@example.com")), Times.Once);

// NSubstitute
emailSender.Received(1).Send(Arg.Is<Email>(m => m.To == "user@example.com"));
// Mockito
verify(emailSender, times(1)).send(argThat(e -> e.getTo().equals("user@example.com")));
// Jest
expect(emailService.send).toHaveBeenCalledTimes(1);
expect(emailService.send).toHaveBeenCalledWith(expect.objectContaining({ to: 'user@example.com' }));

5. Créer des fakes pour tests d'intégration légers

Un fake est une vraie implémentation simplifiée, préférable aux mocks pour les tests qui couvrent plusieurs couches.

public class InMemoryOrderRepository : IOrderRepository
{
    private readonly Dictionary<int, Order> _store = new();

    public Task<Order?> GetById(int id) =>
        Task.FromResult(_store.TryGetValue(id, out var o) ? o : null);

    public Task Save(Order order) { _store[order.Id] = order; return Task.CompletedTask; }
}
class FakeEmailService:
    def __init__(self):
        self.sent: list[Email] = []

    def send(self, email: Email) -> None:
        self.sent.append(email)

6. Pattern Builder pour mocks réutilisables

public class OrderRepositoryMockBuilder
{
    private readonly Mock<IOrderRepository> _mock = new();

    public OrderRepositoryMockBuilder WithOrder(Order order)
    {
        _mock.Setup(r => r.GetById(order.Id)).ReturnsAsync(order);
        return this;
    }

    public OrderRepositoryMockBuilder ThatThrowsOnSave()
    {
        _mock.Setup(r => r.Save(It.IsAny<Order>())).ThrowsAsync(new DbException());
        return this;
    }

    public Mock<IOrderRepository> Build() => _mock;
}

// Usage dans le test
var repo = new OrderRepositoryMockBuilder().WithOrder(OrderMother.Valid()).Build();

Anti-patterns et pièges

Anti-patternProblèmeCorrection
Mocker le code sous testTest circulaire, aucune valeurNe tester que les dépendances
Plus de 3 mocks par testCouplage excessif, test fragileRefactorer le SUT (ex: Facade)
It.IsAny<T>() partoutAssertions trop laxistesMatcher sur les valeurs pertinentes
Partial mock (Spy sur classe concrète)Mélange réel/simulé → faux positifsExtraire une interface
Mock sans VerifyInteraction non testée, mock inutileSupprimer ou ajouter Verify
Setup non utiliséTest trompeurActiver MockBehavior.Strict (Moq)
Fake avec logique complexeLe fake devient un bug en lui-mêmeTester le fake séparément

Bonnes pratiques 2026