我正在学习测试驱动的开发,我注意到它强制松耦合的对象,这基本上是一件好事。但是,这有时也会迫使我为通常不需要的属性提供访问器,并且我认为大多数人都赞同访问器通常是不良设计的标志。在进行TDD时这是不可避免的吗?如何避免测试驱动开发中的访问者?
下面是一个例子,一个实体的简化绘图代码不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);
}
}
实体知道如何绘制自己,这是很好的。所有在一个地方。但是,我正在做TDD,所以我想检查我的实体是否被我将要实现的“fall()”方法正确移动。下面是测试情况可能是什么样子:
@Test
public void entityFalls() {
Entity e = new Entity();
int previousY = e.getY();
e.fall();
assertTrue(previousY < e.getY());
}
我得看看对象的内部(很好,至少在逻辑上)的状态,看看位置是正确更新。因为它实际上是在路上(我不希望我的测试用例依赖于我的图形库),我感动的绘制代码的类“渲染”:
class Renderer {
void drawEntity(Graphics g, Entity e) {
g.drawRect(e.getX(), e.getY(), e.getWidth(), e.getHeight());
}
}
松耦合,不错。我甚至可以用一种以完全不同的方式显示实体的渲染器。但是,我必须公开实体的内部状态,即所有属性的访问器,以便渲染器可以读取它。
我觉得这是TDD特别强迫的。我能做些什么呢?我的设计可以接受吗? Java是否需要C++中的“friend”关键字?
更新:
感谢您的宝贵意见为止!但是,我担心我选择了一个不好的例子来说明我的问题。这是完全编造的,我现在将展示一个更接近我的实际代码:
@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);
}
这是一个基于游戏循环实现游戏的一个实体可以掉下来,等等。如果碰到地面,就会创建一个新的实体。
“mockRenderer”是接口“Renderer”的模拟实现。这个设计部分是由TDD强制的,但也是因为我要在GWT中编写用户界面,并且浏览器中没有明确的绘图(但是),所以我认为这是不可能的实体类承担责任。此外,我想保留未来将游戏移植到本地Java/Swing的可能性。
更新2:
想到这里多一些,也许这是好是怎么回事。也许可以将实体和图形分开,并且实体可以告诉其他对象足以描绘自己。我的意思是,我还能如何实现这种分离?没有它,我真的不知道如何生活。即使是伟大的面向对象的程序员有时也会使用getter/setter对象,特别是对于像实体对象这样的东西。也许getter/setter并不全是邪恶的。你怎么看?
这样看待:如果'Entity'没有'getY',你需要测试它吗?从理论上讲,任何其他代码都无法访问的内容是“实体”的内部特征,对用户而言并不重要,并且通过扩展测试。 – 2010-09-27 21:45:26
但我可能会补充说游戏(这看起来是)对于单元测试来说是一件特别困难的事情。 – 2010-09-27 21:46:25
java的“friend关键字”是默认访问修饰符,同一个包中的每个类都可以访问没有访问修饰符(不公开,私有或受保护)的字段和方法。只要它们使用相同的包结构,测试就可以访问这些字段,因此您可以将这些详细信息保留为包。 – josefx 2010-09-27 21:59:19