Unit Test per testare la creazione di un oggetto dominio

11

Ho un test unitario, che assomiglia a questo:

[Test]
public void Should_create_person()
{
     Assert.DoesNotThrow(() => new Person(Guid.NewGuid(), new DateTime(1972, 01, 01));
}

Sto affermando che qui viene creato un oggetto Person, cioè che la convalida non fallisce. Ad esempio, se Guid è nullo o la data di nascita è precedente a 01/01/1900, la convalida fallirà e verrà lanciata un'eccezione (il test fallirà).

Il costruttore ha il seguente aspetto:

public Person(Id id, DateTime dateOfBirth) :
        base(id)
    {
        if (dateOfBirth == null)
            throw new ArgumentNullException("Date of Birth");
        elseif (dateOfBith < new DateTime(1900,01,01)
            throw new ArgumentException("Date of Birth");
        DateOfBirth = dateOfBirth;
    }

Questa è una buona idea per un test?

Nota : sto seguendo un approccio classicista all'Unità di test del modello di dominio, se questo contiene un rilevamento.

    
posta w0051977 29.01.2018 - 11:39
fonte

2 risposte

18

Questo è un test valido (anche se piuttosto eccessivamente zelante) e a volte lo faccio per testare la logica del costruttore, tuttavia come Laiv ha menzionato nei commenti dovresti chiederti perché.

Se il tuo costruttore ha questo aspetto:

public Person(Guid guid, DateTime dob)
{
  this.Guid = guid;
  this.Dob = dob;
}

C'è un sacco di punti nel testare se lancia? Se i parametri sono assegnati correttamente, posso capire ma il tuo test è piuttosto eccessivo.

Tuttavia, se il tuo test fa qualcosa del genere:

public Person(Guid guid, DateTime dob)
{
  if(guid == default(Guid)) throw new ArgumentException("Guid is invalid");
  if(dob == default(DateTime)) throw new ArgumentException("Dob is invalid");

  this.Guid = guid;
  this.Dob = dob;
}

Quindi il tuo test diventa più pertinente (poiché in realtà stai lanciando eccezioni da qualche parte nel codice).

Una cosa che direi, in generale è una cattiva pratica avere molta logica nel costruttore. La convalida di base (come i controlli null / predefiniti che sto facendo sopra) sono ok. Ma se ti stai collegando ai database e caricando i dati di qualcuno, è da lì che il codice inizia a puzzare davvero ...

Per questo motivo, se il tuo costruttore vale la pena provare (perché c'è molta logica in corso) allora forse qualcos'altro è sbagliato.

Quasi certamente ci saranno altri test che coprono questa classe nei livelli della logica aziendale, costruttori e assegnazioni di variabili quasi certamente otterranno una copertura completa da questi test. Pertanto, potrebbe essere inutile aggiungere test specifici specifici per il costruttore. Tuttavia, nulla è in bianco e nero e non avrei nulla contro questi test se stessi rileggendo il codice - ma mi chiedo se aggiungono molto valore al di sopra e al di là dei test altrove nella soluzione.

Nel tuo esempio:

public Person(Id id, DateTime dateOfBirth) :
        base(id)
    {
        if (dateOfBirth == null)
            throw new ArgumentNullException("Date of Birth");
        elseif (dateOfBith < new DateTime(1900,01,01)
            throw new ArgumentException("Date of Birth");
        DateOfBirth = dateOfBirth;
    }

Non stai solo facendo la validazione, ma stai anche chiamando un costruttore di base. Per me questo fornisce più motivi per avere questi test in quanto hanno la logica costruttore / validazione ora suddivisa su due classi che diminuisce la visibilità e aumenta il rischio di cambiamenti imprevisti.

TLDR

C'è un certo valore in questi test, tuttavia è probabile che la logica di validazione / assegnazione sia coperta da altri test nella soluzione. Se c'è molta logica in questi costruttori che richiede test significativi allora mi suggerisce che c'è un odore di codice sgradevole in agguato lì dentro.

    
risposta data 29.01.2018 - 11:49
fonte
12

Già una buona risposta qui, ma penso che valga ancora una cosa in più.

Quando fai TDD "dal libro", devi prima scrivere un test che chiama il costruttore, anche prima che il costruttore venga implementato. Quel test potrebbe effettivamente assomigliare a quello che hai presentato, anche se ci sarebbe stata una logica di validazione zero all'interno dell'implementazione del costruttore.

Si noti inoltre che per TDD, si dovrebbe scrivere prima un altro test come

  Assert.Throws<ArgumentException>(() => new Person(Guid.NewGuid(), 
        new DateTime(1572, 01, 01));

prima aggiungendo il controllo per DateTime(1900,01,01) al costruttore.

Nel contesto TDD, il test mostrato ha perfettamente senso.

    
risposta data 29.01.2018 - 13:10
fonte

Leggi altre domande sui tag