Proprietà di stub con setter privati ​​per i test


10

Abbiamo l'oggetto

public class MyObject{
    protected MyObject(){}

    public string Property1 {get;private set;}
    public string Property2 {get;private set;}
    public string Property3 {get;private set;}
    public string Property4 {get;private set;}
    public string Property5 {get;private set;}
    public string Property6 {get;private set;}
    public string Property7 {get;private set;}
    public string Property8 {get;private set;}
    public string Property9 {get;private set;}
    public string Property10 {get;private set;}
}

Nel nostro codice di produzione popoliamo questo oggetto tramite automapper. Può accedere alle proprietà e impostarle correttamente.

Ora, quando vogliamo testare questa classe in una futura pipeline, non è possibile popolare le proprietà con valori fittizi (da testare contro).

Ci sono alcune opzioni disponibili.

  • Costruttori personalizzati per accettare i parametri richiesti per i test e impostare le proprietà, attualmente sono richiesti 3 costruttori. Questo non è pulito poiché i costruttori non servono alcuna funzionalità aziendale.

  • Rendi le proprietà virtuali in modo che la classe possa essere cancellata. Ma contrassegnare le proprietà come virtuali non fornisce alcun valore aziendale e inquina la mia classe.

  • Aggiungi un costruttore di oggetti alla classe per costruire internamente l'oggetto. Ancora una volta nessun valore commerciale aggiunto. Forse un po 'più pulito ma ancora molto codice non rilevante negli oggetti dominio.

Hai suggerimenti, consigli o opzioni alternative qui?

Risposte:


8

Hai un numero di opzioni.

  • Vai avanti e usa costruttori personalizzati, proprietà virtuali o un costruttore di oggetti. La logica alla base di ciò è che un oggetto dovrebbe stare da solo, e non dipendere da qualche magia come l'automapper. Una classe che è completamente inutile a meno che non ci sia un po 'di magia in corso non è un pensiero molto ben studiato di classe. Il "valore commerciale" non è l'unico fattore determinante di un buon design.

  • Includi l'automapper nel processo di test. Alcuni diranno che questo non è più un test unitario. Non importa. Il test unitario non è l' unico tipo di test.

  • Implementa qualcosa che fornisca la funzionalità di automapper appositamente per i test. Puoi facilmente scrivere una piccola utility che utilizzerà la riflessione per popolare il tuo oggetto da un dizionario contenente nomi e valori di proprietà.

Dai anche un'occhiata a questa domanda e risposta: preferiresti rendere le cose private interne / pubbliche per i test o utilizzare un qualche tipo di hack come PrivateObject?


3

Non esito a usare la riflessione per cose come questa nei test.

Non mi piace rendere le cose virtuali per deridere in quanto cambia il codice per la ragione sbagliata.

Non conosco automapper ma sono d'accordo con @Mike che includerlo nei test può essere una buona idea. Il test dell'unità di distinzione / test di integrazione non è molto interessante. Se la suite di test sta diventando grande e lenta, dovrai filtrare e classificare le cose per eseguire solo un sottoinsieme ragionevole di tutti i test alla massima frequenza.

L'hacking di esempio con reflection, l'uso di nameof () avrà perf migliori ma poi perderai i tipi .:

public static class TestExtensions
{
    public static void SetProperty<TSource, TProperty>(
        this TSource source,
        Expression<Func<TSource, TProperty>> prop,
        TProperty value)
    {
        var propertyInfo = (PropertyInfo)((MemberExpression)prop.Body).Member;
        propertyInfo.SetValue(source, value);
    }
}

0

A scopo di unit test, utilizzare framework di derisione come Microsoft Fakes, TypeMock e JustMock che forniscono supporto per deridere membri privati.

Dai un'occhiata anche a Smocks (pacchetti @nuget disponibili). Limitazione di Smocks è, non fornirà l'accesso ai membri privati. Ma ha la capacità di deridere membri statici e non virtuali. Inoltre è disponibile gratuitamente.

Un altro modo più semplice è usare PrivateObject / PrivateType.

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.