2010-04-28 15 views
7

¿Cómo abordar las pruebas unitarias de métodos privados?C# Design Questions

Tengo una clase que carga los datos del empleado en una base de datos. Aquí está una muestra:

>

public class EmployeeFacade 
{ 
    public Employees EmployeeRepository = new Employees(); 
    public TaxDatas TaxRepository = new TaxDatas(); 
    public Accounts AccountRepository = new Accounts(); 
    //and so on for about 20 more repositories etc. 

    public bool LoadAllEmployeeData(Employee employee) 
    { 
     if (employee == null) 
      throw new Exception("..."); 


     bool exists = EmployeeRepository.FetchExisting(emps.Id); 
     if (!exists) 
     { 
      EmployeeRepository.AddNew(); 
     } 

     try 
     { 
      EmployeeRepository.Id = employee.Id; 
      EmployeeRepository.Name = employee.EmployeeDetails.PersonalDetails.Active.Names.FirstName; 
      EmployeeRepository.SomeOtherAttribute; 
     } 
     catch() {} 

     try 
     { 
      emps.Save(); 
     } 
     catch(){} 


     try 
     { 
      LoadorUpdateTaxData(employee.TaxData); 
     } 
     catch() {} 

     try 
     { 
     LoadorUpdateAccountData(employee.AccountData); 
     } 
     catch() {} 
     ... etc. for about 20 more other employee objects 

    } 

    private bool LoadorUpdateTaxData(employeeId, TaxData taxData) 
    { 
     if (taxData == null) 
      throw new Exception("..."); 

     ...same format as above but using AccountRepository 

    } 

    private bool LoadorUpdateAccountData(employee.TaxData) 
    { 
     ...same format as above but using TaxRepository 
    } 
} 

estoy escribiendo una aplicación para tomar objetos serializados (. Empleado por ejemplo arriba) y cargar los datos a la base de datos.

Tengo una pregunta del diseño pocos que me gustaría dictámenes sobre: ​​

A - estoy llamando esta clase "EmployeeFacade" porque estoy (tratando?) Para utilizar el patrón de fachada. ¿Es buena idea nombrar el patrón en el nombre de la clase?

B - ¿Es bueno llamar a las entidades concretas de mis clases de capa DAL "Repositorios", p. "EmployeeRepository"?

C - Está utilizando los repositorios de esta manera, es sensato o debo crear un método en el repositorio para tomar, por ejemplo, el empleado y luego cargar los datos desde allí, por ejemplo. EmployeeRepository.LoadAllEmployeeData (Empleado empleado)? Me refiero a la clase cohesiva pero, ¿esto requerirá que el repositorio tenga conocimiento del objeto Employee que puede no ser bueno?

D - ¿Hay alguna manera agradable de no tener que comprobar si un objeto es nulo al comienzo de cada método?

E - Tengo un EmployeeRepository, TaxRepository, AccountRepository declarado público para fines de pruebas de unidad. Estas son en realidad entidades privadas, pero necesito poder sustituirlas por talones para que no escriban en mi base de datos (sobrecargo el método save() para no hacer nada). ¿Hay alguna forma de esto o tengo que exponerlos?

F - ¿Cómo puedo probar los métodos privados, o se hace esto (algo me dice que no es así)?

G- "emps.Name = employee.EmployeeDetails.PersonalDetails.Active.Names.FirstName;" esto rompe la Ley de Demeter, pero ¿cómo ajusto mis objetos para cumplir con la ley?

+3

7 preguntas? !!! –

+0

es una venta fuera de servicio;) – RCIX

+0

Puede ser una buena idea hacer esto como 7 preguntas diferentes, en lugar de agrupar todo. –

Respuesta

1

¿Cómo abordar las pruebas unitarias de métodos privados?

No debe escribir pruebas para métodos privados.

La única forma posible de crear métodos privados es una refactorización de ya probados métodos públicos.

+0

¿No significa eso que haces exactamente lo que hizo? Marcarlos públicamente, probarlos, luego marcarlos en privado una vez que la prueba sea exitosa. – baron

+0

No. Lo que dijiste es conceptualmente incorrecto. Cuando desarrolla clases después de TDD, nunca obtiene métodos no probados. Porque si haces todo correctamente, entonces: 1. crea prueba 2. implementa el método probado 3. obtén la línea verde 4. método refactor implementado (aquí puedes dividir el método en varios métodos privados/protegidos, que ** ya fueron probados * * en el paso 1) – zerkms

+0

tiene sentido. gracias por aclarar – baron

0

A - No creo que sea particularmente malo utilizar el nombre del patrón en el nombre de la clase, aunque sinceramente no sé con qué frecuencia se hace.

F - Creo que zerkms tiene razón, probablemente tengas que hacerlos públicos, pruébalos y hazlos privados cuando estés satisfecho. Una vez que sean privados, aún podría probar los métodos públicos que hacen uso de los métodos privados para garantizar que continúen funcionando.

En cuanto a su DAL y tal, sugiero buscar en LINQ to SQL, disponible en .NET 3.0 y superior. Es un buen marco para manejar la capa de abstracción entre su lógica de negocio y la base de datos. Aquí hay algunos enlaces para ver ...

Quick Tutorial for LINQ to SQL in C# Part 1 of Scott Guthrie's blog

Scott Guthrie tiene un montón de cosas buenas en LINQ, si está interesado, que debe salir más de sus mensajes.

+0

en realidad no me refiero a lo que respondió :-) los métodos privados, como generalmente ** se extrajeron ** de los métodos públicos (que ya están cubiertos por las pruebas), pero ** no solo se cambiaron ** de los públicos a los privados. – zerkms

4

Un - Yo no lo llamaría XXXFacade, pero algo más significativo (que puede, de hecho, significa que debería llamarlo XXXFacade)

B - Yo los llamaría XXXRepository

C: Realmente no entiendo su modelo aquí; está pasando un objeto Employee y asignando sus valores a los valores equivalentes en EmployeeRepository. El Repositorio no debe contener campos de datos: cada instancia del repositorio no representa una fila en la base de datos. El repositorio es una forma de obtener datos dentro y fuera de la base de datos, operando en colecciones de entidades de la base de datos (es decir: el repositorio es la tabla, las entidades son las filas). Esperaría que el objeto Repository tenga un método Save que toma un objeto Employee como parámetro y lo mantiene en la base de datos. Así como un método de carga que tiene un ID y devoluciones y del empleado:

Employee myEmployee = repository.Load(112345); 
myEmployee.Name = "New Name"; 
repository.Save(myEmployee); 

La clase base Repositorio no necesita saber acerca de la implementación específica de la clase Empleado, a través del uso de genéricos y polimorfismo. Eche un vistazo a Sh#rpArchitecture para obtener un buen ejemplo de este patrón.

D - sí, puesto que la lógica común en una clase base abstracta (Repositorio)

E - no hacerlos públicos si deben ser privadas. Si necesita usar la lógica del repositorio en las pruebas de su unidad para simular la obtención de datos, implemente una interfaz común y luego simule esa interfaz en sus pruebas. No necesita probar que el repositorio devuelve los datos correctos ya que los datos son transitorios e inconsistentes en la realidad. Mejor fingir y probar tu comportamiento hace lo que esperas de los datos precargados de un repositorio simulado.

F - Do not. Comportamiento de prueba, no implementación.

G - No creo que este problema exista si examina su arquitectura como se describió anteriormente.

+0

Estoy usando EntitySpaces Vea: http://www.developer.entityspaces.net/documentation/Entity/Create.aspx – guazz

+0

OK - entonces las Colecciones (es decir, EmployeeCollections) es el repositorio, y el Exmployed es la entidad. Si aparece el fragmento de código anterior, ¿es seguro suponer que el empleado que se transfiere a la función es un modelo de vista desde una IU? Tengo muchos problemas para leer su código.Por ejemplo, llama a una variable EmployeeRepository, sin embargo, también parece ser un tipo: public Employees EmployeeRepository = new Employees(); EmployeeRepository emps = new EmployeeRepository(); –

+0

Disculpa, eso fue un error en el código. Lo he actualizado. Sí, el empleado es un modelo de vista que pasó de la interfaz de usuario y debe cargarse en la base de datos. – guazz

0

A - IMO, sí. Inmediatamente le recuerda el patrón y lo ayuda a comprender el código, y esta es quizás una de las prácticas importantes en la escritura de códigos: permitir que otras personas entiendan su código.

B - Prefiero la convención xxDAO (Objeto de acceso a datos).

C - Prefiero la "programación orientada a servicios", es decir, un servicio que "sabe" guardar un empleado y no un "objeto de depósito" que se mezcla entre "modelo" y "control".

D - Tal vez usando Aspect, pero no lo recomiendo.

E - Puede crear una interfaz para los clasificados, e inyectarlos desde "afuera" usando setters (como lo hace la primavera), o conseguirlos de algún tipo de fábrica, de esa manera será fácil para usted reemplace las clases con simulacro, y aún deje a los miembros "privados".

F - Creo que esos métodos deberían extraerse del lado del "empleado de carga" y ser autoservicios. OMI, debe abstraer los objetos de "datos de empleados" (especialmente si tiene 20 de ellos :-)). y escriba un servicio simple que sepa cargar un "objeto de datos del empleado" de cualquier tipo.

esperanza que ayudé,
Shay

1

A - Estoy llamando a esta clase "EmployeeFacade" porque soy utilizar la fachada patrón (intentar?). ¿Es bueno para llamar el patrón en el nombre de la clase?

No creo que probar los métodos privados sea una buena idea; sin embargo, puede probar clases "internas", que son similares a las privadas en el sentido de que los ensamblajes externos no tendrán acceso a ellas, marcándolas como Visible interno para su proyecto de prueba de unidad.

AssemblyInfo.cs - [assembly: InternalsVisibleTo("YourClass.Tests")]

B - ¿Es bueno para llamar a las entidades concretas de mis clases de capa DAL "Depósitos" por ejemplo, "EmployeeRepository"?

Hago esto con frecuencia, no creo que haya nada de malo en ello.

C - Es usar los repositorios de esta manera sensible o debo crear un método en el propio repositorio de Tomemos, por ejemplo, el empleado y luego cargar los datos a partir de ahí, por ejemplo, EmployeeRepository.LoadAllEmployeeData (empleado empleado)? Tengo como objetivo la clase cohesiva y esto requerirá que el repositorio tenga conocimiento del objeto empleado que puede no ser bueno.

A menos que no entiendo correctamente, los mantendría separados. Por lo general, uso mis clases de Repository simplemente como CRUD helpers, escribo un contenedor en el repositorio que expone la funcionalidad que necesita.

D - ¿Hay alguna manera agradable alrededor de no tener que comprobar si un objeto es nula en el comienzo de cada método?

Si lo hay, no sé, me gustaría sólo tiene que utilizar ArgumentNullException()

E - Tengo un EmployeeRepository, TaxRepository, AccountRepository declarados como públicos para las pruebas unitarias propósito. Éstas son en realidad entidades privadas pero necesito poder sustituirlas con talones para que no escriban en mi base de datos (I sobrecargar el método save() para hacer nada). ¿Hay de todos modos alrededor de este o tengo que exponerlos?

Consulte mi respuesta para A, marcándolas como Internas y luego estableciendo InternalsVisible en el ensamblaje de prueba de su unidad. Vea también MSDN.

F - ¿Cómo puedo probar los métodos privados - o está hecho (algo me dice que no)?

Normalmente no pruebo los métodos privados, y las clases privadas que deben probarse las marco como internas y las uso en mi ensamblaje de prueba.

+0

¿Puede explicar por qué no se molesta en probar métodos privados? – baron

+0

Son detalles de implementación: o bien se prueban en virtud de que los métodos públicos se prueban, y los métodos públicos llaman a los métodos privados, o son 'internos' y se muestran como visibles para mi ensamblaje de prueba si es necesario . – Nate

0
  1. Su convención de nombres parece correcta.

  2. Al llamar depósitos de hormigón, está conectando estrechamente el sistema. Pasarles objetos repo en constructor. O use un contenedor DI/IOC.

  3. Si el repositorio es empleado que regresa, lo sabrá. Es posible que desee que el repositorio conozca el contrato para una clase de empleado.

  4. Si obtiene un valor nulo por algo, debe asegurarse de que el código del proveedor no envíe valores nulos.

  5. Puede lograrlo implementando la inyección de dependencia correctamente y usando interfaces.

  6. Los marcos de prueba de unidad estándar no le darán eso, necesitará algo como Moles. Se muestra una muestra en este post

  7. Usa la herencia más que la composición si puedes. Pero si el modelo de objetos lo requiere, entonces estás indefenso en mi opinión.