Estoy aprendiendo el desarrollo basado en pruebas, y he notado que fuerza a los objetos débilmente acoplados, lo cual es básicamente una buena cosa. Sin embargo, esto también a veces me obliga a proporcionar accesos para propiedades que normalmente no necesitaría, y creo que la mayoría de las personas que están en SO están de acuerdo en que los accesorios generalmente son un signo de mal diseño. ¿Es esto inevitable al hacer TDD?¿Cómo evitar los usuarios en desarrollo basado en pruebas?
Aquí se muestra un ejemplo, el código de dibujo simplificado de una entidad sin TDD:
class Entity {
private int x;
private int y;
private int width;
private int height;
void draw(Graphics g) {
g.drawRect(x, y, width, height);
}
}
La entidad sabe cómo dibujar en sí, eso es bueno. Todo en un lugar. Sin embargo, estoy haciendo TDD, así que quiero verificar si mi entidad fue movida correctamente por el método "fall()" que estoy a punto de implementar. Esto es lo que el caso de prueba podría ser:
@Test
public void entityFalls() {
Entity e = new Entity();
int previousY = e.getY();
e.fall();
assertTrue(previousY < e.getY());
}
tengo que mirar a (bueno, al menos lógicamente,) el estado interno del objeto y ver si la posición se actualiza correctamente. Puesto que es en realidad en el camino (no quiero que mis casos de prueba que dependen de mi biblioteca de gráficos), moví el código de dibujo a un "Procesador" clase:
class Renderer {
void drawEntity(Graphics g, Entity e) {
g.drawRect(e.getX(), e.getY(), e.getWidth(), e.getHeight());
}
}
ligeramente agrupado, buena. Incluso puedo reemplazar el renderizador con uno que muestra la entidad de una manera completamente diferente. Sin embargo, tuve que exponer el estado interno de la entidad, es decir, los descriptores de acceso de todas sus propiedades, de modo que el renderizador pudiera leerlo.
Creo que esto fue específicamente forzado por TDD. ¿Qué puedo hacer sobre esto? ¿Es mi diseño aceptable? ¿Necesita Java la palabra clave "friend" de C++?
Actualización:
Gracias por sus valiosas aportaciones hasta el momento! Sin embargo, me temo que he elegido un mal ejemplo para ilustrar mi problema. Esto fue hecho completamente, voy a demostrar que está más cerca de mi código real:
@Test
public void entityFalls() {
game.tick();
Entity initialEntity = mockRenderer.currentEntity;
int numTicks = mockRenderer.gameArea.height
- mockRenderer.currentEntity.getHeight();
for (int i = 0; i < numTicks; i++)
game.tick();
assertSame(initialEntity, mockRenderer.currentEntity);
game.tick();
assertNotSame(initialEntity, mockRenderer.currentEntity);
assertEquals(initialEntity.getY() + initialEntity.getHeight(),
mockRenderer.gameArea.height);
}
Ésta es una implementación basada en bucle del juego de un juego en el que una entidad puede caer, entre otras cosas. Si golpea el suelo, se crea una nueva entidad.
El "mockRenderer" es una implementación simulada de una interfaz "Renderer". Este diseño fue parcialmente forzado por TDD, pero también debido al hecho de que voy a escribir la interfaz de usuario en GWT, y no hay un dibujo explícito en el navegador (todavía), así que no creo que sea posible. la clase de Entidad para asumir esa responsabilidad. Además, me gustaría mantener la posibilidad de llevar el juego a Java/Swing nativo en el futuro.
Actualización 2:
Pensando en esto un poco más, tal vez está bien como es. Tal vez está bien que la entidad y el dibujo estén separados y que la entidad cuente otros objetos lo suficiente como para ser dibujados. Quiero decir, ¿de qué otra manera podría lograr esta separación? Y realmente no veo cómo vivir sin eso. Incluso grandes programadores orientados a objetos usan objetos con getters/setters a veces, especialmente para algo así como un objeto de entidad. Quizás getter/setter no son todos malvados. ¿Qué piensas?
Mírelo de esta manera: si 'Entity' no tiene' getY', ¿debería probarlo? Teóricamente, cualquier cosa que no sea accesible con otro código es interna a 'Entity' y no de valor para el usuario, y por extensión, la prueba. –
Pero podría agregar que los juegos (que parecen ser) son especialmente difíciles de probar en una unidad. –
La "palabra clave friend" de java es el modificador de acceso predeterminado, cada clase en el mismo paquete puede acceder a campos y métodos que no tienen un modificador de acceso (no público, privado o protegido). Las pruebas pueden acceder a estos campos siempre que utilicen la misma estructura de paquete, por lo que puede mantener estos detalles privados para el paquete. – josefx