Supongamos que tengo un Administrador de clase derivado de un Empleado de clase base , y ese Empleado tiene un método getEmail () que hereda el Administrador . ¿Debo probar que el comportamiento del método getEmail () de un gerente es, de hecho, el mismo que el de un empleado?
En el momento en que se escriban estas pruebas, el comportamiento será el mismo, pero, por supuesto, en algún momento en el futuro alguien podría anular este método, cambiar su comportamiento y, por lo tanto, interrumpir mi aplicación. Sin embargo, parece un poco extraño probar esencialmente la ausencia de código de intromisión.
(Tenga en cuenta que probar el método Manager :: getEmail () no mejora la cobertura del código (o incluso cualquier otra métrica de calidad del código (?)) Hasta que Manager :: getEmail () se cree / anule) .
(Si la respuesta es "Sí", sería útil cierta información sobre cómo administrar las pruebas que se comparten entre las clases base y derivada).
Una formulación equivalente de la pregunta:
Si una clase derivada hereda un método de una clase base, ¿cómo expresa (prueba) si espera que el método heredado:
- Comportarse exactamente de la misma manera que la base en este momento (si el comportamiento de la base cambia, el comportamiento del método derivado no lo hace);
- Comportarse exactamente de la misma manera que la base todo el tiempo (si el comportamiento de la clase base cambia, el comportamiento de la clase derivada también cambia); o
- Compórtate como quiera (no te importa el comportamiento de este método porque nunca lo llamas).
fuente
Manager
claseEmployee
fue el primer gran error.Respuestas:
Tomaría el enfoque pragmático aquí: si alguien, en el futuro, anula a Manager :: getMail, entonces es responsabilidad del desarrollador proporcionar el código de prueba para el nuevo método.
Por supuesto, eso solo es válido si
Manager::getEmail
realmente tiene la misma ruta de código queEmployee::getEmail
! Incluso si el método no se anula, podría comportarse de manera diferente:Employee::getEmail
podría llamar a algunos virtuales protegidosgetInternalEmail
que se anulanManager
.Employee::getEmail
podría acceder a algún estado interno (por ejemplo, algún campo_email
), que puede diferir en las dos implementaciones: por ejemplo, la implementación predeterminadaEmployee
podría garantizar que_email
siempre sea así[email protected]
, peroManager
es más flexible en la asignación de direcciones de correo.En tales casos, es posible que un error se manifieste solo
Manager::getEmail
aunque la implementación del método en sí sea la misma. En ese caso, las pruebas porManager::getEmail
separado podrían tener sentido.fuente
Me gustaría.
Si está pensando "bueno, en realidad solo está llamando a Employee :: getEmail () porque no lo anulo, así que no necesito probar Manager :: getEmail ()", entonces no está realmente probando el comportamiento del administrador :: getEmail ().
Pensaría en lo que solo Manager :: getEmail () debería hacer, no si es heredado o anulado. Si el comportamiento de Manager :: getEmail () debe ser devolver lo que Employee :: getMail () devuelve, entonces esa es la prueba. Si el comportamiento es devolver "[email protected]", entonces esa es la prueba. No importa si se implementa por herencia o si se anula.
Lo que importa es que, si cambia en el futuro, su prueba lo detecta y usted sabe que algo se rompió o necesita ser reconsiderado.
Algunas personas pueden estar en desacuerdo con la aparente redundancia en el cheque, pero mi respuesta a eso sería que está probando el comportamiento Employee :: getMail () y Manager :: getMail () como métodos distintos, ya sea que se hereden o no. anulado Si un futuro desarrollador necesita cambiar el comportamiento de Manager :: getMail (), también debe actualizar las pruebas.
Sin embargo, las opiniones pueden variar, creo que Digger y Heinzi dieron justificaciones razonables para lo contrario.
fuente
No lo pruebes por la unidad. Hacer funcional / aceptación probarlo.
Las pruebas unitarias deben probar cada implementación, si no está proporcionando una nueva implementación, entonces cumpla con el principio DRY. Si desea gastar algo de esfuerzo aquí, puede mejorar la prueba de la unidad original. Solo si anula el método debe escribir una prueba unitaria.
Al mismo tiempo, las pruebas funcionales / de aceptación deben garantizar que, al final del día, todo su código haga lo que se supone que debe hacer y, con suerte, detectará cualquier rareza de la herencia.
fuente
Las reglas de Robert Martin para TDD son:
Si sigue estas reglas, no podrá ponerse en posición de hacer esta pregunta. Si el
getEmail
método necesita ser probado, habría sido probado.fuente
Sí, debe probar los métodos heredados porque en el futuro pueden anularse. Además, los métodos heredados pueden llamar a métodos virtuales que se anulan , lo que cambiaría el comportamiento del método heredado no anulado.
La forma en que pruebo esto es creando una clase abstracta para probar la clase base (posiblemente abstracta) o la interfaz, de esta manera (en C # usando NUnit):
Luego, tengo una clase con pruebas específicas para el
Manager
, e integro las pruebas de los empleados de esta manera:La razón por la que puedo hacerlo es el principio de sustitución de Liskov: las pruebas de
Employee
todavía deben pasar cuando se pasa unManager
objeto, ya que se deriva deEmployee
. Así que tengo que escribir mis pruebas solo una vez, y puedo verificar que funcionen para todas las implementaciones posibles de la interfaz o clase base.fuente
setUp()
método de xUnit ! Y casi todos los marcos web MVC que implican anular un método de "índice" también rompen el LSP, que es básicamente todos ellos.class ManagerTests : ExployeeTests
? (es decir, Reflejando la herencia de las clases bajo prueba.) ¿Es principalmente estética, ayudar a ver los resultados?Yo diría que no, ya que en mi opinión sería una prueba repetida que probaría una vez en las pruebas de Empleado y eso sería todo.
Si se anula el método, necesitaría nuevas pruebas para verificar el comportamiento anulado. Ese es el trabajo de la persona que implementa el
getEmail()
método de anulación .fuente
No, no necesita probar métodos heredados. Las clases y sus casos de prueba que dependen de este método se interrumpirán de todos modos si el comportamiento cambia
Manager
.Piense en el siguiente escenario: la dirección de correo electrónico se ensambla como [email protected]:
Usted ha probado esto y funciona bien para usted
Employee
. También has creado una claseManager
:Ahora tiene una clase
ManagerSort
que clasifica a los administradores en una lista basada en su dirección de correo electrónico. Por supuesto, supones que la generación de correo electrónico es la misma que enEmployee
:Escribes una prueba para tu
ManagerSort
:Todo funciona bien Ahora viene alguien y anula el
getEmail()
método:Ahora que pasa? Tu
testManagerSort()
fallará porque elgetEmail()
deManager
fue anulado. Investigará en este problema y encontrará la causa. Y todo sin escribir un caso de prueba separado para el método heredado.Por lo tanto, no necesita probar métodos heredados.
De lo contrario, por ejemplo, en Java, tendría que probar todos los métodos heredados de
Object
liketoString()
,equals()
etc. en cada clase.fuente