> ## Documentation Index
> Fetch the complete documentation index at: https://docs.devinenterprise.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Ajouter des tests unitaires à votre service de paiement

export const UseCaseHero = ({title, description, prompt, category, features, devinUrl, agent, intent, playbookId, type}) => {
  const encodedPrompt = encodeURIComponent(prompt || '');
  const tag = 'docs-use-case-gallery';
  const utm = 'utm_source=docs&utm_medium=use-case-gallery&utm_campaign=hero-cta';
  const agentParams = (agent ? '&agent=' + agent : '') + (intent ? '&intent=' + intent : '') + (playbookId ? '&playbookId=' + playbookId : '');
  const devinHref = type === 'schedule' ? 'https://app.devin.ai/settings/schedules/create?' + utm + agentParams + (prompt ? '&prompt=' + encodedPrompt : '') : type === 'review' ? 'https://app.devin.ai/review?' + utm : agent === 'ada' ? 'https://app.devin.ai/search?' + utm + '&noSubmit=true' + (prompt ? '&prompt=' + encodedPrompt : '') : devinUrl ? devinUrl.includes('?') ? devinUrl + '&' + utm + agentParams : devinUrl + '?' + utm + agentParams : prompt ? 'https://app.devin.ai/?tags=' + tag + '&' + utm + agentParams + '&prompt=' + encodedPrompt : 'https://app.devin.ai/?' + utm + agentParams;
  const buttonLabel = type === 'schedule' ? 'Schedule in Devin ↗' : type === 'review' ? 'Set Up Devin Review ↗' : agent === 'advanced' ? 'Try in Devin ↗' : agent === 'dana' ? 'Try in Dana ↗' : agent === 'ada' ? 'Try in Ask Devin ↗' : 'Try in Devin ↗';
  const featureList = features ? features.split(',').map(f => f.trim()) : [];
  return <div className="uc-hero">
      <div className="uc-hero-inner">
        <div className="uc-hero-left">
          <h1 className="uc-hero-title">{title}</h1>
          <p className="uc-hero-desc">{description}</p>
          <div>
            <a href={devinHref} target="_blank" rel="noopener noreferrer" className="try-in-devin-btn">
              {buttonLabel}
            </a>
          </div>
        </div>
        <div className="uc-hero-meta">
          <div className="uc-meta-item">
            <span className="uc-meta-label">Author</span>
            <span className="uc-meta-value">Cognition</span>
          </div>
          <div className="uc-meta-item">
            <span className="uc-meta-label">Category</span>
            <span className="uc-meta-value">{category}</span>
          </div>
          {featureList.length > 0 && <div className="uc-meta-item">
              <span className="uc-meta-label">Features</span>
              <span className="uc-meta-value">{featureList.join(', ')}</span>
            </div>}
        </div>
      </div>
    </div>;
};

export const PromptBlock = ({children, type, agent, intent, playbookId}) => {
  var utm = 'utm_source=docs&utm_medium=use-case-gallery&utm_campaign=prompt-block';
  var tag = 'docs-use-case-gallery';
  var agentParams = (agent ? '&agent=' + agent : '') + (intent ? '&intent=' + intent : '') + (playbookId ? '&playbookId=' + playbookId : '');
  var label = type === 'schedule' ? 'Schedule in Devin' : type === 'playbook' ? 'Create Playbook' : type === 'knowledge' ? 'Add to Knowledge' : agent === 'advanced' ? 'Try in Devin' : agent === 'dana' ? 'Try in Dana' : agent === 'ada' ? 'Try in Ask Devin' : 'Try in Devin';
  var buildUrl = function (text) {
    var encoded = encodeURIComponent(text);
    if (type === 'schedule') return 'https://app.devin.ai/settings/schedules/create?' + utm + agentParams + '&prompt=' + encoded;
    if (type === 'playbook') return 'https://app.devin.ai/settings/playbooks/create?' + utm + '&body=' + encoded;
    if (type === 'knowledge') return 'https://app.devin.ai/knowledge?' + utm + '&body=' + encoded;
    if (agent === 'ada') return 'https://app.devin.ai/search?' + utm + '&noSubmit=true&prompt=' + encoded;
    return 'https://app.devin.ai/?tags=' + tag + '&' + utm + agentParams + '&prompt=' + encoded;
  };
  const ref = React.useRef(null);
  const [href, setHref] = React.useState('#');
  React.useEffect(() => {
    if (!ref.current) return;
    var codeEl = ref.current.querySelector('pre code');
    if (codeEl) {
      var text = codeEl.textContent.trim();
      if (text) setHref(buildUrl(text));
    }
    var header = ref.current.querySelector('[data-component-part="code-block-header"]');
    if (header && !header.querySelector('.prompt-block-devin-link')) {
      var link = document.createElement('a');
      link.href = href;
      link.target = '_blank';
      link.rel = 'noopener noreferrer';
      link.className = 'prompt-block-devin-link';
      link.style.cssText = 'display:inline-flex;align-items:center;gap:6px;text-decoration:none;color:#fff;font-size:11px;font-weight:500;padding:4px 10px;border-radius:6px;white-space:nowrap;background:#317CFF;transition:background 0.2s;margin-left:8px;';
      link.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg> ' + label;
      link.onmouseenter = function () {
        link.style.background = '#2968D9';
      };
      link.onmouseleave = function () {
        link.style.background = '#317CFF';
      };
      header.appendChild(link);
    }
    var existingLink = ref.current.querySelector('.prompt-block-devin-link');
    if (existingLink && href !== '#') existingLink.href = href;
  });
  return <div className="prompt-block" ref={ref}>{children}</div>;
};

<UseCaseHero title="Ajouter des tests unitaires à votre service de paiements" description="Élaborez un playbook de rédaction de tests pour le traitement des paiements et laissez Devin couvrir les flux de débit, la logique de remboursement et les gestionnaires de webhooks avec des tests unitaires complets." prompt="Écrivez des tests unitaires pour toutes les fonctions non testées dans src/services/PaymentService.ts. Couvrez processCharge, issueRefund et handleWebhook. Simulez le SDK Stripe et les appels à la base de données. Chaque fonction doit avoir des tests pour les cas de réussite, d'erreur et les cas limites, comme les débits en double et les remboursements partiels. Visez 90 % de couverture des lignes de code." category="Qualité du code" features="Playbooks" />

<div className="uc-detail-wrapper">
  <Tip>Vous ne souhaitez pas effectuer cette configuration manuellement ? Collez un lien vers cette page dans une session Devin et demandez-lui de tout configurer pour vous.</Tip>

  <Steps>
    <Step title="Rédiger un playbook de tests dédié aux paiements">
      A [playbook](/fr/product-guides/creating-playbooks) formalise les conventions de test de votre équipe pour que Devin écrive les tests comme le feraient vos ingénieurs. Le code de paiement présente des contraintes spécifiques — idempotence, précision des montants, réessais de passerelle, mocks conformes au PCI — donc un playbook dédié aux paiements détecte des problèmes qu’un playbook générique manquerait.

      **Option 1 : Rédiger vous‑même le playbook.** Allez dans [**Settings > Playbooks > Create playbook**](https://app.devin.ai/settings/playbooks/create?utm_source=docs\&utm_medium=use-case-gallery) et définissez vos standards de test :

      <PromptBlock type="playbook">
        ```txt Payments Test Playbook theme={null}
        ## Procédure
        1. Lire le module de paiement ciblé et identifier toutes les fonctions exportées
        2. Étudier les fichiers de test existants dans src/services/__tests__/ pour repérer les schémas récurrents
        3. Créer un fichier de test nommé {Module}.test.ts dans __tests__/ à côté de la source
        4. Écrire les tests en utilisant le modèle AAA (Arrange, Act, Assert)
        5. Mocker le SDK Stripe avec jest.mock('stripe') — ne jamais appeler de véritables APIs de paiement
        6. Mocker les appels à la base de données avec les helpers de src/test/helpers.ts
        7. Utiliser des entiers en cents pour toutes les valeurs monétaires — jamais de dollars en virgule flottante
        8. Tester l’idempotence : appeler processCharge deux fois avec la même clé d’idempotence ne doit pas créer de prélèvements en double
        9. Couvrir les cas de succès, d’erreur et les cas limites pour chaque fonction
        10. Lancer `npm test -- --coverage` et vérifier au moins 90 % de couverture de lignes pour le fichier

        ## Spécifications
        - Framework : Jest avec TypeScript
        - Mocking : jest.mock() pour le SDK Stripe, jest.spyOn() pour les méthodes internes
        - Devise : toujours utiliser des entiers en cents (par ex. 2999 et non 29.99)
        - Assertions : privilégier les matchers spécifiques (toHaveBeenCalledWith, toThrow)
        - Nommage : describe('PaymentService') > describe('processCharge') > it('charges a valid card')

        ## Actions interdites
        - Ne pas appeler Stripe, PayPal ou toute autre passerelle de paiement en production dans les tests
        - Ne pas utiliser d’arithmétique en virgule flottante pour les calculs monétaires
        - Ne pas modifier le module source pour le rendre plus facile à tester
        - Ne pas ignorer les cas limites liés à l’idempotence ou à la logique de réessai
        ```
      </PromptBlock>

      **Option 2 : Laisser Devin créer le playbook pour vous.** Décrivez vos conventions de test et Devin générera un playbook complet :

      <PromptBlock agent="advanced">
        ```txt Generate a payments test playbook theme={null}
        Create a playbook for writing unit tests for our payment modules in
        src/services/. We use Jest with TypeScript, mock the Stripe SDK with
        jest.mock('stripe'), and use integer cents for all currency values.
        Look at our existing tests in src/services/__tests__/ to learn our
        patterns (AAA style, specific matchers, describe/it naming). The
        playbook should cover mocking strategy, idempotency testing, currency
        precision rules, and forbidden actions like calling real payment APIs.
        ```
      </PromptBlock>

      Ajoutez ensuite votre meilleur fichier de test existant (par ex. `src/services/__tests__/UserService.test.ts`) comme entrée [Knowledge](/fr/product-guides/knowledge) pour que Devin dispose d’un exemple concret du style de votre équipe.
    </Step>

    <Step title="Identifier le code de paiement non testé">
      Avant de demander à Devin d’analyser des fichiers spécifiques, identifiez d’abord les lacunes dans vos modules de paiement. Demandez à Devin d’exécuter votre outil de couverture et de mettre en évidence les pires cas :

      <PromptBlock>
        ```txt Analyze payment module coverage theme={null}
        Run npm test -- --coverage and list every file under src/services/
        that handles payments, billing, or subscriptions with less than 80%
        line coverage. Sort by coverage ascending so I can see the biggest
        gaps first.
        ```
      </PromptBlock>

      Devin exécute la suite de tests dans son terminal, analyse le rapport de couverture et vous fournit une liste classée par ordre de priorité :

      ```
      File                                | Lines | Uncovered functions
      ------------------------------------|-------|------------------------------
      src/services/PaymentService.ts      |  34%  | processCharge, issueRefund, handleWebhook
      src/services/SubscriptionService.ts |  41%  | renewSubscription, cancelTrial, proratePlan
      src/services/InvoiceService.ts      |  52%  | generateInvoice, applyPromoCode, calculateTax
      src/services/PayoutService.ts       |  58%  | initiateTransfer, reconcileSettlement
      ```
    </Step>

    <Step title="Demander à Devin de rédiger des tests pour PaymentService">
      Démarrez une nouvelle session, joignez votre playbook de tests de paiement (vous verrez une pastille bleue confirmant qu’il est bien joint), et indiquez à Devin quel module couvrir :

      <PromptBlock>
        ```txt Write tests for PaymentService theme={null}
        Write unit tests for all untested functions in
        src/services/PaymentService.ts. Focus on processCharge,
        issueRefund, and handleWebhook. Each function needs tests for
        success, error, and edge cases. Mock the Stripe SDK — never call
        real APIs. Use integer cents for all currency values. Run the
        tests and verify 90%+ line coverage for this file.
        ```
      </PromptBlock>

      Devin lit le module, étudie vos tests existants pour en dégager des modèles, écrit un fichier de tests complet en suivant votre playbook, puis l’exécute :

      ```
      PASS src/services/__tests__/PaymentService.test.ts
        PaymentService
          processCharge
            ✓ charges a valid credit card and returns a receipt (14ms)
            ✓ uses integer cents to avoid floating-point errors (6ms)
            ✓ rejects duplicate charges with the same idempotency key (5ms)
            ✓ retries on Stripe gateway timeout up to 3 times (18ms)
            ✓ throws InsufficientFundsError for declined cards (4ms)
          issueRefund
            ✓ refunds full amount for a completed charge (8ms)
            ✓ refunds partial amount in cents when specified (6ms)
            ✓ prevents refund exceeding the original charge amount (3ms)
            ✓ throws AlreadyRefundedError on duplicate refund attempts (4ms)
          handleWebhook
            ✓ processes charge.succeeded events and updates order status (7ms)
            ✓ processes charge.refunded events and credits the customer (6ms)
            ✓ verifies Stripe webhook signature before processing (3ms)
            ✓ ignores unrecognized event types without error (2ms)

      Coverage: 94% lines | 91% branches | 100% functions
      ```

      Devin ouvre une pull request (PR) avec le fichier de test et un résumé de la couverture de code dans la description.
    </Step>

    <Step title="Traitez les modules de paiement restants">
      Examinez la première PR. Si la stratégie de mock ou le style des assertions n’est pas tout à fait adapté, mettez à jour votre playbook avant de l’exécuter sur davantage de modules — un seul cycle de feedback vous évitera de corriger le même problème sur plusieurs PR.

      Ensuite, parcourez votre liste de lacunes :

      <PromptBlock>
        ```txt Cover SubscriptionService theme={null}
        Write tests for src/services/SubscriptionService.ts using the same
        playbook. Focus on renewSubscription, cancelTrial, and proratePlan.
        Test proration math using integer cents. Target 90%+ line coverage.
        ```
      </PromptBlock>

      Pour aller plus vite, demandez à Devin de lancer des sessions parallèles — une par module de paiement — toutes basées sur le même playbook. Ou [planifiez](/fr/product-guides/scheduled-sessions) une session hebdomadaire qui détecte automatiquement les modules de paiement passés sous votre seuil de couverture et rédige les tests correspondants.
    </Step>
  </Steps>
</div>
