Un test unitario è considerato fragile se fallisce quando cambia la logica aziendale?


27

Si prega di consultare il codice qui sotto; verifica se una persona con Sesso femminile è ammissibile all'offerta1:

[Fact]
public void ReturnsFalseWhenGivenAPersonWithAGenderOfFemale()
{
    var personId = Guid.NewGuid();
    var gender = "F";
    var person = new Person(personId, gender);

    var id = Guid.NewGuid();
    var offer1 = new Offer1(id,"Offer1");
    Assert.False(offer1.IsEligible(person));
}

Questo test unitario ha esito positivo. Tuttavia, fallirà se in futuro verrà offerto alle donne "Offer1".

È accettabile dire: se la logica aziendale che circonda l'offerta 1 cambia, allora il test unitario deve cambiare. In alcuni casi (per alcune offerte) la logica aziendale viene modificata nel database in questo modo:

update Offers set Gender='M' where offer=1;

e in alcuni casi nel modello di dominio come questo:

if (Gender=Gender.Male)
{
  //do something
}

Si noti inoltre che in alcuni casi la logica del dominio dietro offre modifiche periodiche e in alcuni casi no.


2
Pensa da un'altra prospettiva: vuoi avere dei test che non falliscono quando cambi logica nel sistema sotto il test?
Fabio,

Risposte:


77

Questo non è fragile nel solito senso. Un test unitario è considerato fragile se si interrompe a causa di cambiamenti di implementazione che non influiscono sul comportamento in test. Ma se la logica aziendale stessa cambia, si suppone che un test di questa logica si interrompa.

Detto questo, se la logica aziendale cambia spesso spesso, forse non è appropriato codificare le aspettative nei test unitari. Invece è possibile verificare se le configurazioni nel database influiscono sulle offerte come previsto.

Il nome del test Returns False When Given A Person With A Gender Of Femalenon descrive una regola aziendale. Una regola commerciale sarebbe qualcosa di simile Offers Applicable to M should not be applied to persons of gender F.

Quindi potresti scrivere un test che confermi che se un'offerta è definita applicabile solo alle persone di tipo M, una persona di tipo F non sarà indicata come ammissibile per essa. Questo test assicurerà che la logica funzioni anche se cambia la configurazione delle offerte specifiche.


@JaquesB, quindi non sarebbe un test unitario? o sarebbe? Credo che sarebbe un test di integrazione se fosse coinvolto il database. È giusto? Stai dicendo di non usare i test unitari se la logica aziendale cambia molto?
w0051977,

@ w0051977: dipende da come si scrive il test. Se il test include effettivamente la modifica di qualcosa in un database, sarebbe un test di integrazione.
Jacques B

3
@ w0051977 idea migliore: non è necessario che il repository dipenda dal componente responsabile dell'implementazione delle regole di business. Avere un'orchestrazione di livello superiore che chiama il repository e quindi richiama le regole aziendali. Ora puoi testare le regole aziendali separatamente.
Formica P,

5
@ w0051977 ovviamente lo è - i test specificano il comportamento. Se le regole che governano il comportamento di un componente cambiano, i test devono cambiare per riflettere il cambiamento nel comportamento. Ciò che non dovrebbe essere necessario modificare sono i test che specificano comportamenti diversi da ciò che sta cambiando. Se il comportamento è determinato dal database, un test che copre qualche altro codice è intrinsecamente non correlato e non dovrebbe essere necessario modificarlo a meno che la logica del database non rientri nell'ambito del test. Tale ambito è da definire e la semantica del fatto che si tratti di un unit test o di un test di integrazione non è davvero importante.
Formica P

3
@ w0051977 estendendo in qualche modo questa idea, se la logica aziendale cambia o un bug viene corretto e i test non devono essere modificati, è un segno che i test non coprono casi sufficienti e dovrebbero generalmente essere espansi.
Morgen,

14

Quando la proprietà è definita nel database di produzione (o un clone per il test), questo non è un test unitario . Un test unitario verifica un'unità di lavoro e non richiede un particolare stato esterno per funzionare. Ciò presuppone che Offer1nel database sia definito come un'offerta esclusivamente maschile. Questo è uno stato esterno. Quindi questo è più un test di integrazione , in particolare un sistema o un test di accettazione . Si noti che i test di accettazione spesso non sono scritti (non eseguiti in un framework di test ma eseguiti manualmente da esseri umani).

Quando la proprietà è definita nel modello di dominio con ifun'istruzione, lo stesso test è un unit test. E potrebbe essere fragile. Ma il vero problema è che il codice è fragile. Come regola generale, il codice sarà più resiliente se il comportamento aziendale è configurabile anziché codificato. Perché una distribuzione urgente per correggere un piccolo errore di codifica dovrebbe essere rara. Ma un requisito aziendale che cambia senza preavviso è solo un martedì (qualcosa che accade settimanalmente).

È possibile che si stia utilizzando un framework di unit test per eseguire il test. Ma i framework di unit test non si limitano all'esecuzione di unit test. Possono anche eseguire test di integrazione.

Se stavi scrivendo un test unitario, creeresti entrambi persone offer1da zero senza fare affidamento sullo stato del database. Qualcosa di simile a

[Fact]
public void ReturnsFalseWhenGivenAPersonWithAGenderOfFemale()
{
    var personId = Guid.NewGuid();
    var gender = "F";
    var person = new Person(personId, gender);

    var id = Guid.NewGuid();
    var offer1 = new Offer1(id, "ReturnsFalseWhenGivenAPersonWithAGenderOfFemale");
    offer1.markLimitedToGender("M");

    Assert.False(offer1.IsEligible(person));
}

Si noti che ciò non cambia in base alla logica aziendale. Non sta affermando che offer1rifiuta le femmine. Sta facendo offer1il tipo di offerta che rifiuta le femmine.

È possibile creare e configurare il database come parte del test. In C #, utilizzando NUnit o in JUnit di Java, si imposta il database in un Setupmetodo. Presumibilmente il tuo framework di test ha una nozione simile. In questo metodo, è possibile inserire record nel database con SQL.

Se è difficile scrivere un codice che sostituisca un database di prova con il database di produzione, ciò sembra una debolezza del test nell'applicazione. Per i test, sarebbe meglio usare qualcosa come l'iniezione di dipendenza che consente la sostituzione. Quindi è possibile scrivere test indipendenti dalle attuali regole aziendali.

Un vantaggio collaterale di questo è che spesso è più facile per il proprietario dell'azienda (non necessariamente il proprietario dell'azienda, più come la persona responsabile di questo prodotto nella gerarchia aziendale) configurare direttamente le regole aziendali. Perché se si dispone di questo tipo di framework tecnico, è facile consentire al proprietario dell'azienda di utilizzare un'interfaccia utente (UI) per configurare l'offerta. Il proprietario dell'azienda selezionerebbe la limitazione nell'interfaccia utente ed emetterebbe la markLimitedToGender("M")chiamata. Quindi, quando l'offerta viene mantenuta nel database, questa viene archiviata. Ma non è necessario archiviare l'offerta per utilizzarla. Quindi i tuoi test potrebbero creare e configurare un'offerta che non esiste nel database.

Nel tuo sistema come descritto, il proprietario dell'azienda dovrebbe inviare una richiesta al gruppo tecnico, che emetterebbe l'SQL appropriato e aggiornerà i test. Oppure il gruppo tecnico deve modificare il codice e i test (o i test, quindi il codice). Sembra un approccio piuttosto pesante. Puoi farlo. Ma il tuo software (non solo i tuoi test) sarebbe meno fragile se non dovessi farlo.

TL; DR : puoi scrivere test come questo, ma potresti essere meglio scrivere il tuo software in modo da non doverlo fare.


Attendo la libertà di migliorare alcuni dettagli minori nella tua risposta. Per favore, controlla se ho capito bene le tue intenzioni.
Doc Brown,

Non ci sono indicazioni nel post originale che sia coinvolto un database. Quindi affermare che presuppone che Offer1 sia già nel database è bizzarro.
Winston Ewert,

2
@ WinstonEwert: c'è un'indicazione chiara, devi leggere la domanda più attentamente. Non me ne sono reso conto nemmeno alla prima lettura, ma è proprio ciò di cui parla l'OP.
Doc Brown,

@ mdfst13, mi dispiace averlo perso. Tuttavia, ciò che l'OP sta dicendo è che le condizioni sono talvolta in un database e talvolta nel modello di dominio. La tua risposta è perfetta per il primo caso e irriverentemente nel secondo. Se modificherai la tua risposta per chiarire questo punto, rimuoverò il mio frettoloso voto negativo.
Winston Ewert,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.