La classe dovrebbe validare un argomento utilizzando un metodo / servizio esterno con test delle unità?

0

Ho una classe che viene eseguita come servizio e restituisce il codice html di un sito web quando viene fornito con un URL.

Il codice:

public interface IHtmlDownloader
{
    IWebProxy Proxy { get; set; }
    string UserAgent { get; set; }

    string GetHtml(string url)
}

public class HtmlDownloader : IHtmlDownloader
{
    WebClient _client;

    public IWebProxy Proxy
    {
        get { return _client.Proxy; }
        set { _client.Proxy = value; }
    }

    public string UserAgent
    {
        get
        { return _client.Headers[HttpRequestHeader.UserAgent].ToString(); }
        set
        { _client.Headers[HttpRequestHeader.UserAgent] = value; }
    }

    public HtmlDownloder()
    {
        _client = new WebClient();
    }

    public string GetHtml(string url)
    {
        this.CheckValidity(url);

        var htmlOfWebsite = _client.DownloadString(url);

        return htmlOfWebsite;
    }
}

Ora non sono sicuro di come esattamente dovrei convalidare l'URL. So che non dovrebbe essere nullo o spazio bianco o altro, ma dovrei convalidare che si tratta in effetti di un URL costruito correttamente?

Così facendo si otterrebbe un metodo che eseguirà la convalida. Potrei semplicemente creare un metodo statico pubblico all'interno di HtmlDownloader in modo che il chiamante possa convalidare l'URL, o forse nemmeno uno statico ( GetUrl(string url) è chiamato dopo che l'oggetto è stato istanziato), ma sarebbe sbagliato per almeno due ragioni:

  1. Sarebbe un validatore applicabile in così tanti posti e dovrebbe essere disponibile per altre classi.
  2. Non riesco a contrarre un metodo statico con un'interfaccia.

Inoltre, non dovrebbe essere reso statico, ma piuttosto un servizio, quindi potrei testare l'unità HtmlDownloader (il servizio verrebbe iniettato tramite il costruttore).

Tutte queste idee sono piuttosto terribili e sto iniziando a pensare che non dovrei convalidare nulla, ma semplicemente creare un blocco try/catch che catturerebbe l'eccezione appropriata e poi lanciare un'eccezione UrlNotFoundException con l'eccezione originale fornita attraverso InnerException.

Tuttavia, sarebbe bello verificare la validità dell'URL. Ma a chi e come? Il chiamante deve essere iniettato con il servizio adeguato? Ma allora, cosa succede se l'URL è generato più alto del chiamante e il chiamante lo passa semplicemente su HtmlDownloader? Dove dovrei convalidare l'URL? O è il semplice fatto che l'URL deve passare attraverso alcuni livelli un odore di codice?

    
posta moskalak 04.03.2014 - 17:54
fonte

2 risposte

0

A mio parere, il servizio dovrebbe convalidare l'URL in modo che possa restituire un errore che descrive l'abuso. Non è possibile fare affidamento sul chiamante che esegue la convalida e assicurarsi che passi i parametri validi. Immagino che sia probabilmente preso come letto e non stavi suggerendo che la cosa che chiama il servizio definisce la convalida.

Proseguendo, la convalida delle proprietà generiche dovrebbe essere gestita con un meccanismo generico.

Supponendo innanzitutto che stai implementando un numero limitato di servizi:

Se la tua lingua ti fornisce il costrutto, usalo direttamente in questo codice.

Se non è disponibile, direi che uno può essere chiamato staticamente e chiamarlo invece - I.E. trattalo come se fosse un costrutto di linguaggio standard.

È semplice e diretto e inizia a causare un problema solo se hai molti servizi e molte duplicazioni.

In termini di TDD:

Supponiamo che tu abbia implementato il metodo statico UrlValidator::validate , sia che si tratti di un'implementazione completa che di un involucro del costrutto linguistico disponibile per renderlo più piacevole da usare. Non è importante, è solo un metodo statico.

Questo può essere testato in modo esauriente nel proprio test di unità. Puoi lanciare un migliaio di casi di test per assicurarti che si comporti esattamente come richiesto.

Quindi, quando stai testando il servizio HtmlDownloader , devi solo testare la convalida in modo sufficiente per convincerti che stai utilizzando correttamente UrlValidator . Ad esempio, assicurati che passi un indirizzo email ovviamente valido, un indirizzo email ovviamente non valido fallisce.

Questo è un po 'simile a quello che faresti con l'iniezione delle dipendenze, tranne per il fatto che hai un'ipotesi: test fai abbastanza per convincerti che la tua ipotesi è corretta. Con DI si verifica direttamente che l'ipotesi sia corretta.

E abbastanza equo, DI potrebbe rendere questo un po 'più facile da testare e il test più robusto, ma sembra eccessivo per un problema così semplice, in particolare se stai implementando solo un numero limitato di servizi.

Secondo me dovrebbe essere HtmlDownloader s responsiblity per definire che ha bisogno di un URL valido, e se questo è il caso non è solo il caso di iniettare un UrlValidator , ma di iniettare qualcosa che possa validare qualunque cosa tu chieda a.

Che mi porta al caso "se stai implementando molti servizi"

In questa situazione è probabilmente utile implementare un pacchetto di convalida dei parametri generico. Qualcosa che puoi iniettare in HtmlDownloader , in modo che possa passare in una configurazione e i dati ricevuti e ottenere uno stato di convalida.

es. potresti passare qualcosa come (pseudo-codice):

{ url = { required = true
        , dataType = ValidationTypes::URL }
}

L'implementazione della convalida vive al di fuori del servizio, ma il servizio definisce in modo chiaro ciò che deve essere convalidato e quali sono i requisiti.

Questo riduce enormemente il test richiesto perché ora devi solo verificare che la configurazione sia corretta, piuttosto che testarne l'effetto.

Mi piacciono le implementazioni come questa, solo che a volte è eccessivo per il tipo di soluzione che ti serve.

È una questione di scala e flessibilità richiesta.

    
risposta data 07.03.2014 - 12:55
fonte
-1

Per quanto possibile, cerca di rendere le tue lezioni il più possibile indipendenti durante i test unitari, riducendo al minimo il riferimento incrociato delle funzioni e aiutandoti a eseguire il debug, dopo aver eseguito tutte le prove dell'unità potresti creare funzioni di utilità indipendente in grado di gestire operazioni che verranno incrociate come riferimento dalle tue classi ...

    
risposta data 27.11.2014 - 12:45
fonte

Leggi altre domande sui tag