2009-05-27 7 views
6

Sto provando a testare un metodo di entità ordine chiamato AddItem e sto cercando di assicurarmi che non sia possibile aggiungere elementi duplicati. Ecco qualche esempio di codice:Domanda di setter privato del collaudo di unità (C#)

[Test] 
public void ItemCannotBeAddedTwiceToOrder() 
{ 
    Order o = new Order(); 
    Item i = new Item("Bike"); 

    o.AddItem(i); 
    o.AddItem(i); 

    Assert.AreEqual(o.ItemCount, 1, "A duplicate item was added."); 
} 

public void AddItem(Item newItem) 
{ 
    if(!CheckForDuplicateItem(newItem)) 
     _items.Add(newItem); 
} 

public bool CheckForDuplicateItem(Item newItem) 
{ 
    foreach(Item i in _items) 
    { 
     if(i.Id == newItem.Id) 
      return true; 
    } 

    return false; 
} 

Così qui è il mio problema: come faccio a impostare la nuova voce setter privato Id nel metodo di prova in modo che il metodo di CheckForDuplicateItem funzionerà? Non voglio rendere pubblico quel membro per buone pratiche di codifica, immagino. Sono semplicemente stupido e ho bisogno di rendere l'oggetto dell'entità un impostore pubblico? O devo usare la riflessione? Grazie

Nota - Sto usando NHibernate per la persistenza

risposta

8

io di solito uso di riflessione per questo scopo. Qualcosa del genere funziona:

typeof(Item).GetProperty("Id").SetValue(i, 1, null); 

dove 1 è l'ID che si desidera impostare per l'istanza newItem.

Nella mia esperienza, raramente dovrai impostare l'Id, quindi è meglio lasciare il setter privato. Nei pochi casi in cui è necessario impostare l'ID a scopo di test, è sufficiente utilizzare Reflection.

+0

Grazie mille. Farò un tentativo. – CalebHC

7

Poiché si controlla il comportamento del proprio ordine, è possibile utilizzare gli oggetti fittizi come elementi. Usando oggetti finti puoi definire le tue asserzioni su cosa succederà ai tuoi oggetti finti e testarli anche tu. In questo caso puoi definire due oggetti mock per ciascuno degli elementi e aspettarti che venga chiamato il loro identificatore di ID e restituisca un valore univoco. Quindi puoi testare il comportamento dell'Ordine e controllare se id getter dell'elemento viene chiamato come tu previsto. Si consiglia di utilizzare Rhino Mocks di Ayende

+0

Buon punto. Prenderò in giro beffardo. Ho sentito molte cose positive su Rhino Mocks. – CalebHC

0

Un'altra soluzione è rendere accessibili i membri privati ​​derivando dalla classe ed esponendo il membro nella classe derivata. Questo è un bel po 'di overhead per i test e Visual Studio ha solo build-in support for private methods.

0

Penso che potrebbe mancare il punto qui. Stai evitando più aggiunte perché non vuoi più chiamate al DB? Penso che NHibernate te lo dica gratis. In alternativa, dovresti usare un Set? Quali sono le implicazioni per il chiamante che un articolo potrebbe o non potrebbe essere aggiunto?

Se non c'è problema di persistenza, è sufficiente aggiungere due elementi distinti con lo stesso ID e confermare di avere solo il primo. Ci deve essere un modo per scoprire quali elementi sono nell'ordine, o non ci sarebbe alcun motivo per aggiungerli ...

3

Mentre la risposta di Praveen è corretta e senza dubbio serve per l'uso singolo, manca qualche tipo di sicurezza per utilizzalo ripetutamente nei test su un modello di dominio forte. Perciò ho avvolto in un metodo di estensione che permette questo tipo di chiamata sicura di impostare un valore:

var classWithPrivateSetters= new ClassWithPrivateSetters(); 
classWithPrivateSetters.SetPrivate(cwps => cwps.Number, 42); 

goccia questo nella vostra montaggio di prova e vi sono buone per andare

public static class PrivateSetterCaller 
{ 
    public static void SetPrivate<T,TValue>(this T instance, Expression<Func<T,TValue>> propertyExpression, TValue value) 
    { 
     instance.GetType().GetProperty(GetName(propertyExpression)).SetValue(instance, value, null); 
    } 

    private static string GetName<T, TValue>(Expression<Func<T, TValue>> exp) 
    { 
     MemberExpression body = exp.Body as MemberExpression; 

     if (body == null) 
     { 
      UnaryExpression ubody = (UnaryExpression)exp.Body; 
      body = ubody.Operand as MemberExpression; 
     } 

     return body.Member.Name; 
    } 
}