Stubbing Properties con setters privados para pruebas

10

Tenemos el objeto

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

En nuestro código de producción, completamos este objeto a través de automapper. Puede acceder a las propiedades y configurarlas correctamente.

Ahora, cuando queremos probar esta clase en una tubería futura, no es posible llenar las propiedades con valores ficticios (para ser probados).

Hay algunas opciones disponibles.

  • Constructores personalizados para aceptar los parámetros requeridos para las pruebas y establecer las propiedades, actualmente se requieren 3 constructores. Esto no está limpio ya que los constructores no sirven a ninguna funcionalidad comercial.

  • Haga que las propiedades sean virtuales para que la clase pueda ser troceada Pero marcar las propiedades virtuales no proporciona ningún valor comercial y contamina mi clase.

  • Agregue un generador de objetos a la clase para construir internamente el objeto. Nuevamente, no hay valor comercial agregado. Quizás un poco más limpio, pero aún hay mucho código no relevante en los objetos de dominio.

¿Alguna sugerencia, consejo u opciones alternativas aquí?

JMan
fuente

Respuestas:

8

Tienes un número de opciones.

  • Siga adelante y use constructores personalizados, propiedades virtuales o un generador de objetos. La razón detrás de esto es que un objeto debe sostenerse por sí mismo, y no depender de algo de magia como el automapper. Una clase que es completamente inútil a menos que haya algo de magia no es una clase muy bien pensada. El "valor comercial" no es el único determinante de un buen diseño.

  • Incluya el automapper en el proceso de prueba. Algunos dirán que esto ya no es una prueba unitaria. No importa. Las pruebas unitarias no son el único tipo de prueba.

  • Implemente algo que proporcione la funcionalidad de automapper específicamente para pruebas. Puede escribir fácilmente una pequeña utilidad que utilizará la reflexión para llenar su objeto de un diccionario que contiene nombres y valores de propiedades.

También eche un vistazo a esta pregunta y respuesta: ¿Prefiere hacer cosas privadas internas / públicas para las pruebas, o usar algún tipo de pirateo como PrivateObject?

Mike Nakis
fuente
3

No dudo en usar la reflexión para cosas como esta en las pruebas.

No me gusta hacer que las cosas sean virtuales para burlarse, ya que cambia el código por la razón incorrecta.

No conozco automapper, pero estoy de acuerdo con @Mike en que incluirlo en las pruebas puede ser una buena idea. La prueba de unidad de distinción / prueba de integración no es muy interesante. Claro, si el conjunto de pruebas se está volviendo grande y lento, necesitará filtrar y clasificar las cosas para ejecutar solo un subconjunto sensible de todas las pruebas a la frecuencia más alta.

El hack de muestra usando la reflexión, usando nameof () tendrá un mejor rendimiento, pero luego perderá los tipos .:

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);
    }
}
Johan Larsson
fuente
0

Para fines de pruebas unitarias, utilice marcos de simulación como Microsoft Fakes, TypeMock y JustMock que brindan soporte para burlarse de miembros privados.

Por favor, eche un vistazo a Smocks (paquetes disponibles @nuget) también. La limitación de Smocks es que no proporcionará acceso a miembros privados. Pero tiene la capacidad de burlarse de miembros estáticos y no virtuales. También está disponible de forma gratuita.

Otra forma más simple es usar PrivateObject / PrivateType.

Karthik V
fuente