Share via


Unit test e integration nelle app per le API minime

Di Fiyaz Bin Hasan e Rick Anderson

Introduzione ai test di integrazione

I test di integrazione valutano i componenti di un'app in un livello più ampio rispetto agli unit test. Gli unit test vengono usati per testare componenti software isolati, ad esempio singoli metodi di classe. I test di integrazione confermano che due o più componenti dell'app interagiscono per produrre un risultato previsto, possibilmente includendo ogni componente necessario per elaborare completamente una richiesta.

Questi test più ampi vengono usati per testare l'infrastruttura e l'intero framework dell'app, spesso inclusi i componenti seguenti:

  • Database
  • File system
  • Dispositivi di rete
  • Pipeline di richiesta-risposta

Gli unit test usano componenti creati, noti come falsi o oggetti fittizi, al posto dei componenti dell'infrastruttura.

A differenza degli unit test, i test di integrazione:

  • Usare i componenti effettivi usati dall'app nell'ambiente di produzione.
  • Richiedere più codice ed elaborazione dati.
  • L'esecuzione richiede più tempo.

Pertanto, limitare l'uso dei test di integrazione agli scenari di infrastruttura più importanti. Se un comportamento può essere testato usando uno unit test o un test di integrazione, scegliere lo unit test.

Nelle discussioni sui test di integrazione, il progetto testato viene spesso chiamato System Under Test o "SUT" per brevità. "SUT" viene usato in questo articolo per fare riferimento all'app ASP.NET Core sottoposta a test.

Non scrivere test di integrazione per ogni permutazione dei dati e dell'accesso ai file con database e file system. Indipendentemente dal numero di posizioni in cui un'app interagisce con database e file system, un set incentrato di test di integrazione di lettura, scrittura, aggiornamento ed eliminazione è in genere in grado di testare adeguatamente i componenti di database e file system. Usare unit test per i test di routine della logica del metodo che interagiscono con questi componenti. Negli unit test, l'uso di infrastrutture false o mocks comporta un'esecuzione più veloce dei test.

test di integrazione di ASP.NET Core

I test di integrazione in ASP.NET Core richiedono quanto segue:

  • Un progetto di test viene usato per contenere ed eseguire i test. Il progetto di test ha un riferimento a SUT.
  • Il progetto di test crea un host Web di test per SUT e usa un client del server di test per gestire le richieste e le risposte con SUT.
  • Un test runner viene usato per eseguire i test e segnalare i risultati del test.

I test di integrazione seguono una sequenza di eventi che includono i normali passaggi di test Arrange, Act e Assert :

  1. L'host Web SUT è configurato.
  2. Viene creato un client del server di test per inviare richieste all'app.
  3. Viene eseguito il passaggio Disponi test: l'app di test prepara una richiesta.
  4. Viene eseguito il passaggio di test act : il client invia la richiesta e riceve la risposta.
  5. Viene eseguito il passaggio di test Assert : la risposta effettiva viene convalidata come passaggio o esito negativo in base a una risposta prevista .
  6. Il processo continua fino a quando non vengono eseguiti tutti i test.
  7. I risultati del test vengono segnalati.

In genere, l'host Web di test viene configurato in modo diverso rispetto al normale host Web dell'app per le esecuzioni di test. Ad esempio, per i test è possibile usare un database diverso o impostazioni di app diverse.

I componenti dell'infrastruttura, ad esempio l'host Web di test e il server di test in memoria (TestServer), vengono forniti o gestiti dal pacchetto Microsoft.AspNetCore.Mvc.Testing. L'uso di questo pacchetto semplifica la creazione e l'esecuzione dei test.

Il Microsoft.AspNetCore.Mvc.Testing pacchetto gestisce le attività seguenti:

  • Copia il file delle dipendenze (.deps) dal SUT nella directory del progetto di bin test.
  • Imposta la radice del contenuto sulla radice del progetto di SUT in modo che i file statici e le pagine/visualizzazioni vengano trovati quando vengono eseguiti i test.
  • Fornisce la classe WebApplicationFactory per semplificare il bootstrap del SUT con TestServer.

La documentazione degli unit test descrive come configurare un progetto di test e uno strumento di esecuzione dei test, oltre a istruzioni dettagliate su come eseguire test e consigli su come assegnare un nome ai test e alle classi di test.

Separare gli unit test dai test di integrazione in progetti diversi. Separazione dei test:

  • Assicura che i componenti di test dell'infrastruttura non siano inclusi accidentalmente negli unit test.
  • Consente il controllo su quale set di test vengono eseguiti.

Il codice di esempio in GitHub fornisce un esempio di unit test e di integrazione in un'app per le API minima.

Tipi di implementazione IResult

I tipi di implementazione pubblica IResult nello Microsoft.AspNetCore.Http.HttpResults spazio dei nomi possono essere usati per unit test dei gestori di route minimi quando si usano metodi denominati anziché espressioni lambda.

Il codice seguente usa la NotFound<TValue> classe :

[Fact]
public async Task GetTodoReturnsNotFoundIfNotExists()
{
    // Arrange
    await using var context = new MockDb().CreateDbContext();

    // Act
    var result = await TodoEndpointsV1.GetTodo(1, context);

    //Assert
    Assert.IsType<Results<Ok<Todo>, NotFound>>(result);

    var notFoundResult = (NotFound) result.Result;

    Assert.NotNull(notFoundResult);
}

Il codice seguente usa la Ok<TValue> classe :

[Fact]
public async Task GetTodoReturnsTodoFromDatabase()
{
    // Arrange
    await using var context = new MockDb().CreateDbContext();

    context.Todos.Add(new Todo
    {
        Id = 1,
        Title = "Test title",
        Description = "Test description",
        IsDone = false
    });

    await context.SaveChangesAsync();

    // Act
    var result = await TodoEndpointsV1.GetTodo(1, context);

    //Assert
    Assert.IsType<Results<Ok<Todo>, NotFound>>(result);

    var okResult = (Ok<Todo>)result.Result;

    Assert.NotNull(okResult.Value);
    Assert.Equal(1, okResult.Value.Id);
}

Risorse aggiuntive