简介
目前我有以下形式的内容:
Tetris class ---> FallingPiece class ----> Piece class
一个Piece
可以是Square
,一个T
等。它包含有关其形状、旋转形状、大小等的信息。
FallingPiece
类基本上包含一个引用 Piece
的属性。 (俄罗斯方 block 游戏中当前掉落的棋子)并且可能会有当前的 (x, y)
位置及其颜色。
我最初的设计是具有以下形式:
class Tetris {
private IPieceGenerator pieceGenerator;
private FallingPiece fallingPiece;
...
public Tetris(IPieceGenerator pieceGenerator) {
this.pieceGenerator = pieceGenerator;
...
}
private void someMethodThatNeedsAFallingPiece() {
if (fallingPiece == null) {
Piece piece = pieceGenerator.Generate();
fallingPiece = new FallingPiece(piece);
}
...
}
}
这当然有一个问题,如果我稍后想要对我的俄罗斯方 block 类进行单元测试并知道其中 (x, y)
我当前的董事会位置FallingPiece
是的,我不能。我记得在神话般的Misko Hevery's The Clean Code Talks中看到过这个“问题”。 .
第一个问题
似乎就是这样,因为我给出了 Tetris
类创建的责任FallingPiece
对象,然后我无法引用它们(我已经通过构造函数注入(inject) Piece
Factory,而不是 FallingPiece
Factory!)。
现在,我至少可以看到两种方法来解决这个问题:
- 我可以定义一个
internal
(C#)/package-protected
(Java)FallingPiece
的 getter属性,这样我就可以轻松测试它。这可能看起来无害,但我觉得它不太优雅。 - 我可以不传递它
Piece
工厂,通证FallingPiece
工厂。然后我可以控制工厂返回哪些对象并通过它访问它们。大家觉得这样怎么样?常用吗?
还有其他方法可以解决这个问题吗?
还有第二个问题
与我最初实现了 FallingPiece
相关的事实成为不可变类型。这意味着每次我想更新FallingPiece
在 Tetris
上的位置例如,我必须创建一个新的 FallingPiece
俄罗斯方 block 上的实例和属性现在将指向新的 FallingPiece
。例如,如果我想访问FallingPiece
,这可能是一个大问题。通过FallingPieceFactory
引用被传递到Tetris
类(class)。在我看来,在尝试测试类时,如果滥用不可变数据类型可能会带来很多麻烦,对吧?或者这首先是对不可变数据类型的错误使用?
谢谢
最佳答案
可能的解决方案:
- 不要将实现连接在一起,而是使用接口(interface)。
- 引入一个生成器来创建 FallingPiece
这样您就可以使用接口(interface)的模拟对象来测试所有内容。将 someMethodThatNeedsAFallingPiece 的可见性更改为 protected 以在测试用例中访问它或使用反射调用它(邪恶)。
public class Tetris {
private IPieceGenerator pieceGenerator;
private IFallingPiece fallingPiece;
private IFallingPieceGenerator fallingPieceGenerator;
public Tetris(IPieceGenerator pieceGenerator, IFallingPieceGenerator fallingPieceGenerator) {
this.pieceGenerator = pieceGenerator;
this.fallingPieceGenerator = fallingPieceGenerator;
}
protected void someMethodThatNeedsAFallingPiece() {
if (fallingPiece == null) {
IPiece piece = pieceGenerator.Generate();
fallingPiece = fallingPieceGenerator.generate(piece);
}
}
}
这是您的单元测试:
public class TetrisTest {
private IPieceGenerator pieceGeneratorMock;
private IFallingPieceGenerator fallingPieceGeneratorMock;
private IFallingPiece fallingPieceMock;
private IPiece pieceMock;
private Tetris tetris;
@Before
public void init() {
this.pieceGeneratorMock = EasyMock.createMock(IPieceGenerator.class);
this.fallingPieceGeneratorMock = EasyMock.createMock(IFallingPieceGenerator.class);
this.fallingPieceMock = EasyMock.createMock(IFallingPiece.class);
this.pieceMock = EasyMock.createMock(IPiece.class);
this.tetris = new Tetris(pieceGeneratorMock, fallingPieceGeneratorMock);
}
@Test
public void testSomeMethodThatNeedsAFallingPiece() {
expect(pieceGeneratorMock.Generate()).andReturn(pieceMock);
expect(fallingPieceGeneratorMock.generate(pieceMock)).andReturn(fallingPieceMock);
replay(fallingPieceMock, fallingPieceGeneratorMock, pieceGeneratorMock, pieceMock);
tetris.someMethodThatNeedsAFallingPiece();
verify(fallingPieceMock, fallingPieceGeneratorMock, pieceGeneratorMock, pieceMock);
}
}
关于c# - 关于在将进行单元测试的类中使用 new 运算符的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3444818/