Vai al contenuto principale

Tutorial pytest-mock: guida per principianti al mocking in Python

Impara a usare pytest-mock: dal setup e i fondamenti alle tecniche avanzate per test pratici.
Aggiornato 29 set 2026  · 15 min leggi

Scopri con l'IA

ChatGPTClaudePerplexity

Man mano che i tuoi progetti Python crescono in complessità e dimensioni, cresce anche l'importanza di test solidi. I test aiutano a garantire che il codice funzioni come previsto e mantengano la qualità in tutte le parti dell'applicazione.

Quando la tua applicazione interagisce con diversi tipi di database o API esterne come OpenAI GPT o Anthropic Claude, testare queste dipendenze può diventare impegnativo. È qui che il mocking torna utile.

Il mocking ti permette di simulare oggetti, funzioni e comportamenti, rendendo i test più isolati e prevedibili. Ti consente anche di concentrarti sulla funzione specifica che vuoi testare isolandola da dipendenze che potrebbero introdurre complessità o comportamenti imprevisti.

In questo blog imparerai a usare pytest-mock, un potente plugin per pytest, per implementare i mock nei tuoi test Python in modo efficiente. Alla fine di questo tutorial, sarai pronto ad aggiungere tecniche di mocking ai tuoi casi di test, rendendoli più potenti.

Che cos'è pytest-mock?

pytest-mock è un plugin per il popolare framework di testing Python pytest che fornisce accesso semplice alle funzionalità di mocking. Si basa sul unittest integrato di Python, semplificando il processo di mocking durante i test.

logo del framework pytest

logo del framework pytest

pytest-mock migliora la leggibilità e rende facile implementare i mock nei test con un approccio più nativo a pytest. Che tu debba fare il mock di singole funzioni, metodi di classe o oggetti interi, pytest-mock offre la flessibilità necessaria per test efficaci senza complessità aggiuntiva.

Un motivo chiave della leggibilità di pytest-mock è il suo stile di test dichiarativo, che ti permette di specificare direttamente nei test chiamate e comportamenti attesi. Questo rende il codice più facile da scrivere, leggere e mantenere, restando al contempo resiliente a cambiamenti d'implementazione che non alterano il comportamento esterno del codice.

Configurare pytest-mock

Creare un ambiente virtuale per il tuo progetto è una best practice per garantire l'isolamento delle dipendenze. Se non hai ancora configurato un nuovo ambiente, puoi crearne uno conda usando i seguenti comandi:

# create a conda environment
conda create --name yourenvname python=3.11
# activate conda environment
conda activate yourenvname

Installare pytest e pytest-mock

Installa pytest e pytest-mock direttamente nel tuo ambiente usando pip o conda:

pip install pytest
pip install pytest-mock

Confermare l'installazione

Dopo aver installato pytest e pytest-mock, verifica che l'installazione sia andata a buon fine eseguendo il seguente comando:

pytest --version

Basi del mocking con pytest-mock

Il mocking può essere visto come la creazione di una versione "fittizia" di un componente che imita il comportamento reale. 

È particolarmente utile quando devi testare come funzioni o classi interagiscono con componenti esterni come database o API esterne.

Introduzione al mocking

Un oggetto mock simula il comportamento di un oggetto reale. È spesso usato nei test unitari per isolare i componenti e testarne il comportamento senza eseguire il codice da cui dipendono. 

Ad esempio, potresti usare un oggetto mock per simulare una chiamata al database senza connetterti realmente al database.

Il mocking è particolarmente utile quando l'oggetto reale:

  • È difficile da creare o configurare.
  • Richiede molto tempo per essere usato (ad esempio, accedere a un database remoto).
  • Ha effetti collaterali da evitare durante i test (ad esempio, inviare email, sostenere costi).

Usare la fixture mocker

pytest-mock fornisce una fixture mocker, che rende facile creare e controllare oggetti mock. La fixture mocker è potente e si integra nei tuoi test pytest.

💡Cosa sono le fixture? 

In Python, le fixture sono componenti riutilizzabili che preparano e smontano le risorse necessarie ai test, come database o file. Garantiscono coerenza, riducono duplicazioni e semplificano il setup dei test.

Vediamo un esempio:

def fetch_weather_data(api_client):
    response = api_client.get("https://api.weather.com/data")
    if response.status_code == 200:
        return response.json()
    else:
        return None

La funzione fetch_weather_data dipende da un'API meteo esterna per recuperare i dati, ma chiamare l'API durante i test potrebbe comportare costi non necessari. 

Per evitarlo, puoi usare il mocking per simulare il comportamento dell'API nei test, assicurando che l'applicazione venga testata senza effettuare chiamate esterne. Ecco come il mocking può aiutare:

def test_fetch_weather_data(mocker):
    # Create a mock API client
    mock_api_client = mocker.Mock()

    # Mock the response of the API client
    mock_response = mocker.Mock()
    mock_response.status_code = 200
    mock_response.json.return_value = {"temperature": "20C", "condition": "Sunny"}

    # Set the mock API client to return the mocked response
    mock_api_client.get.return_value = mock_response

    # Call the function with the mock API client
    result = fetch_weather_data(mock_api_client)

    # Assert that the correct data is returned
    assert result == {"temperature": "20C", "condition": "Sunny"}

    mock_api_client.get.assert_called_once_with("https://api.weather.com/data")

In test_fetch_weather_data() usiamo la fixture mocker per creare un client API mock mock_api_client e un mock_response. La risposta mock è configurata per imitare una vera chiamata API restituendo un codice di stato 200 e dati JSON con informazioni meteo. 

Il metodo get del client API mock è impostato per restituire questa risposta mock. Quando si testa fetch_weather_data(), interagisce con il client API mock invece di effettuare una chiamata API reale. 

Questo approccio assicura che la funzione si comporti come previsto, mantenendo i test veloci, economici e indipendenti dai sistemi esterni. 

L'istruzione assert finale verifica che i dati restituiti corrispondano all'output atteso.

L'ultima riga, mock_api_client.get.assert_called_once_with("https://api.weather.com/data"), controlla specificamente che il metodo get del client API mock sia stato chiamato esattamente una volta e con l'argomento corretto. Questo aiuta a confermare che la funzione interagisce con l'API nel modo previsto, aggiungendo un ulteriore livello di validazione al test.

Casi d'uso comuni per pytest-mock

Il mocking è utile in vari scenari, inclusi i test unitari in cui devi isolare funzioni o componenti specifici. Vediamo alcuni casi d'uso comuni:

1. Fare il mock di funzioni o metodi di classe

Potresti dover testare una funzione che dipende da altre funzioni o metodi, ma vuoi controllarne il comportamento durante i test. Il mocking ti permette di sostituire quelle dipendenze con comportamenti predefiniti:

# production code
def calculate_discount(price, discount_provider):
    discount = discount_provider.get_discount()
    return price - (price * discount / 100)

# test code
def test_calculate_discount(mocker):
    # Mock the get_discount method
    mock_discount_provider = mocker.Mock()
    mock_discount_provider.get_discount.return_value = 10  # Mocked discount value
    # Call the function with the mocked dependency
    result = calculate_discount(100, mock_discount_provider)
    # Assert the calculated discount is correct
    assert result == 90
    mock_discount_provider.get_discount.assert_called_once()

In questo test, il metodo get_discount() di discount_provider è mockato per restituire un valore predefinito (10%). Questo isola la funzione calculate_discount() dall'implementazione reale di discount_provider.

2. Mockare codice dipendente dal tempo

Quando testi funzioni che coinvolgono tempo o ritardi, fare il mock di metodi legati al tempo aiuta a evitare attese o manipolare il tempo reale:

# production code
import time
def long_running_task():
    time.sleep(5)  # Simulate a long delay
    return "Task Complete"

# test code
def test_long_running_task(mocker):
    # Mock the sleep function in the time module
    mocker.patch("time.sleep", return_value=None)
    # Call the function
    result = long_running_task()
    # Assert the result is correct
    assert result == "Task Complete"

Qui, time.sleep è patchato per evitare l'effettivo ritardo durante i test. Questo assicura che il test venga eseguito rapidamente senza attendere 5 secondi.

3. Mockare attributi di oggetti

A volte potresti voler fare il mock del comportamento degli attributi o delle proprietà di un oggetto per simulare stati diversi durante i test:

# production code
class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    @property
    def is_adult(self):
        return self.age >= 18

# test code
def test_user_is_adult(mocker):
    # Create a User object
    user = User(name="Alice", age=17)
    # Mock the is_adult property
    mocker.patch.object(User, "is_adult", new_callable=mocker.PropertyMock, return_value=True)
    # Assert the mocked property value
    assert user.is_adult is True

In questo esempio, la proprietà is_adult() della classe User è mockata per restituire True, indipendentemente dall'età reale. Questo è utile per testare scenari dipendenti dallo stato dell'oggetto.

Tecniche avanzate di mocking con pytest-mock

Una volta che hai preso confidenza con il mocking di base, pytest-mock offre funzionalità avanzate per gestire scenari più complessi.

1. Effetti collaterali (side effect) dei mock

I mock possono simulare non solo valori di ritorno, ma anche replicare comportamenti come sollevare eccezioni o cambiare dinamicamente i valori restituiti. Questo si ottiene aggiungendo "side effect" ai mock:

import pytest

# production code
def process_payment(payment_gateway, amount):
    response = payment_gateway.charge(amount)
    if response == "Success":
        return "Payment processed successfully"
    else:
        raise ValueError("Payment failed")

# test code
def test_process_payment_with_side_effects(mocker):
    # Mock the charge method of the payment gateway
    mock_payment_gateway = mocker.Mock()
    # Add side effects: Success on first call, raise exception on second call
    mock_payment_gateway.charge.side_effect = ["Success", ValueError("Insufficient funds")]
    # Test successful payment
    assert process_payment(mock_payment_gateway, 100) == "Payment processed successfully"
    # Test payment failure
    with pytest.raises(ValueError, match="Insufficient funds"):
        process_payment(mock_payment_gateway, 200)
    # Verify the mock's behavior
    assert mock_payment_gateway.charge.call_count == 2

La proprietà side_effect permette al mock di restituire valori diversi o sollevare eccezioni nelle chiamate successive. In questo esempio, la prima chiamata a charge restituisce "Success", mentre la seconda solleva un ValueError. È utile per testare vari scenari in un unico test.

2. Spiare le funzioni

Lo spying ti consente di tracciare come una funzione reale è stata chiamata, incluso il numero di chiamate e gli argomenti passati. È particolarmente utile quando vuoi assicurarti che una funzione venga chiamata come previsto pur eseguendo la sua logica originale:

# production code
def log_message(logger, message):
    logger.info(message)
    return f"Logged: {message}"

# test code
def test_log_message_with_spy(mocker):
    # Spy on the info method of the logger
    mock_logger = mocker.Mock()
    spy_info = mocker.spy(mock_logger, "info")
    # Call the function
    result = log_message(mock_logger, "Test message")
    # Assert the returned value
    assert result == "Logged: Test message"
    # Verify the spy behavior
    spy_info.assert_called_once_with("Test message")
    assert spy_info.call_count == 1

Il metodo spy() tiene traccia di come un metodo reale viene chiamato senza alterarne il comportamento. Qui facciamo lo spy del metodo info del logger per assicurarci che venga chiamato con il messaggio corretto, lasciando comunque che il metodo venga eseguito normalmente.

Best practice per usare pytest-mock

Il mocking è potente, ma usarlo senza attenzione può portare a problemi nei test. Ecco alcune best practice che ti consiglio di seguire:

1. Evita l'over-mocking

Usare troppi mock può portare a test fragili che si rompono quando il codice interno cambia, anche se il comportamento esterno no. Cerca di mockare solo ciò che è necessario e affidati alle implementazioni reali quando possibile.

L'over-mocking può rendere i test strettamente accoppiati ai dettagli d'implementazione del tuo codice, il che significa che anche un refactoring minore può causare fallimenti inutili. Concentrati invece sul mocking solo delle parti del sistema che sono esterne o che hanno effetti collaterali significativi.

2. Usa convenzioni di naming chiare

Quando fai mock di oggetti, usa nomi descrittivi per i tuoi mock. Questo rende i test più leggibili e facili da mantenere. Ad esempio, invece di usare nomi generici come mock_function(), usa qualcosa di più descrittivo, come mock_api_response().

Convenzioni di naming chiare aiutano gli altri a comprendere lo scopo di ogni mock, riducendo la confusione e facilitando la manutenzione della suite di test nel tempo.

3. Mantieni i test semplici e focalizzati

Ogni test dovrebbe concentrarsi su un singolo comportamento o scenario. Questo semplifica il debug e mantiene i test facili da capire. Un test ben focalizzato è più propenso a fornire un feedback chiaro quando qualcosa va storto, rendendo più facile identificare e correggere i problemi.

Conclusione

In questo blog abbiamo esplorato le basi del mocking con pytest-mock e imparato a usarlo per migliorare i nostri test Python. Abbiamo coperto tutto, dal mocking di base di funzioni e metodi a tecniche più avanzate come aggiungere side effect e spiare le funzioni.

Il mocking è uno strumento essenziale per creare test affidabili e manutenibili, soprattutto quando si lavora con sistemi complessi o dipendenze esterne. Integrando pytest-mock nei tuoi progetti, puoi scrivere test più isolati, prevedibili e facili da mantenere.

Per continuare ad ampliare le tue competenze di testing, ti consiglio di dare un'occhiata al corso gratuito Introduction to Testing in Python su Datacamp.

FAQs

In cosa il mocking è diverso dal testare componenti reali?

Il mocking sostituisce componenti reali con quelli simulati, consentendo test isolati ed economici senza dipendere da sistemi esterni o dati reali.

Posso usare pytest-mock con altri framework di testing?

pytest-mock è progettato specificamente per pytest e si integra perfettamente con esso, ma si basa su unittest.mock di Python, che può essere usato in modo indipendente.

Le fixture sono obbligatorie per il mocking in pytest-mock?

Sebbene fixture come mocker migliorino l'efficienza del mocking, puoi comunque usare direttamente unittest.mock in pytest senza fixture, anche se con un'integrazione minore.


Moez Ali's photo
Author
Moez Ali
LinkedIn
Twitter

Data Scientist, fondatore e creatore di PyCaret

Argomenti
Python

Approfondisci Python con questi corsi!

Corso

Introduzione a Python per sviluppatori

3 ore
185.5K
Impara le basi della programmazione in Python. Non serve sapere niente prima!
Vedi i dettagliRight Arrow
Inizia Il Corso
Mostra altroRight Arrow
Correlato

blog

Tokenizzazione nel NLP: come funziona, sfide e casi d'uso

Guida al preprocessing NLP nel machine learning. Copriamo spaCy, i transformer di Hugging Face e come funziona la tokenizzazione in casi d'uso reali.
Abid Ali Awan's photo

Abid Ali Awan

10 min

blog

Che cos'è Snowflake? Guida per principianti alla piattaforma dati cloud

Esplora le basi di Snowflake, la piattaforma dati cloud. Scopri la sua architettura, le sue funzionalità e come integrarla nelle tue pipeline di dati.
Tim Lu's photo

Tim Lu

12 min

blog

I 15 migliori server MCP remoti che ogni AI builder dovrebbe conoscere nel 2026

Scopri i 15 migliori server MCP remoti che stanno trasformando lo sviluppo AI nel 2026. Scopri come migliorano automazione, ragionamento, sicurezza e velocità dei workflow.
Abid Ali Awan's photo

Abid Ali Awan

15 min

Mostra AltroMostra Altro