2009-05-27 12 views
6

Estoy tratando de probar un método de entidad de Orden llamado AddItem y estoy tratando de asegurarme de que no se puedan agregar elementos duplicados. Aquí es un código de ejemplo:Prueba de Unidad Pregunta de Organizador Privado (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; 
} 

Así que aquí está mi problema: cómo fijo colocador privada Id del elemento nuevo en el método de prueba por lo que el método CheckForDuplicateItem va a funcionar? No quiero hacer público ese miembro para buenas prácticas de codificación, supongo. ¿Estoy siendo estúpido y necesito hacer que la entidad Item tenga un setter de Id público? ¿O necesito usar el reflejo? Gracias

Nota - estoy usando NHibernate para la persistencia

Respuesta

8

que suelen utilizar la reflexión para este propósito. Algo así funcionará:

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

donde 1 es el id que desea establecer para la instancia newItem.

En mi experiencia, rara vez necesitarás configurar el Id, por lo que es mejor dejar el setter en privado. En los pocos casos en los que necesita establecer el Id para fines de prueba, simplemente use Reflection.

+0

muchas gracias. Voy a intentar esto. – CalebHC

7

Como está comprobando el comportamiento de su pedido, puede utilizar objetos simulados como elementos. Usando objetos simulados puedes definir tus afirmaciones de lo que va a pasar con tus objetos simulados y probarlos también. En este caso, puede definir dos objetos simulados para cada uno de los elementos y esperar que se invoque su identificador y devolverá un valor único. Luego puede probar el comportamiento del pedido y verificar si se llama el identificador del elemento como usted esperado. Recomiendo usar Rhino Mocks por Ayende

+0

Buen punto. Buscaré burla. He escuchado muchas cosas buenas sobre Rhino Mocks. – CalebHC

0

Otra solución es hacer que los miembros privados sean accesibles derivando de la clase y exponiendo al miembro en la clase derivada. Esto es bastante sobrecarga para probar y Visual Studio solo tiene build-in support for private methods.

0

Creo que se puede estar perdiendo el punto aquí. ¿Está evitando adiciones múltiples porque no quiere múltiples llamadas al DB? Creo que NHibernate te da eso gratis. Alternativamente, ¿deberías estar usando un Set? ¿Cuáles son las implicaciones para la persona que llama que un elemento puede o no ser agregado?

Si no hay problemas de persistencia, puede agregar dos elementos distintos con la misma ID y confirmar que solo tiene la primera. Debe haber alguna forma de detectar qué elementos están en la orden, o no tendría sentido agregarlos ...

3

Si bien la respuesta de Praveen es correcta y sin duda sirve para el uso único, se pierde algún tipo de seguridad para utilizar esto en pruebas sobre un modelo de dominio fuerte una y otra vez. Por lo tanto, lo envolvió en un método de extensión que le permite esta llamada segura tipo permite establecer un valor:

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

gota esto en su conjunto de prueba y que son buenos para ir

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; 
    } 
}