关于在将进行单元测试的类中使用 new 运算符的问题
目前
我有以下形式的东西:
Tetris class ---> FallingPiece class ----> Piece class
Piece
可以是 Square
、T
等。它包含有关其形状和形状的信息。它的旋转形状、大小等。
FallingPiece
类基本上包含一个引用 Piece
的属性(俄罗斯方块游戏中当前下落的棋子),并且可能会有其当前的(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);
}
...
}
}
这当然有一个问题,如果我稍后想要对我的俄罗斯方块类进行单元测试并了解棋盘的哪个 (x, y)
位置我目前的 FallingPiece
是,我不能。我记得在精彩的 Misko Hevery 的《整洁代码讲座》 中看到过这个“问题”。
第一个问题
似乎是,当我赋予 Tetris
类创建 FallingPiece
对象的责任时,我无法引用它们(我已经通过通过构造函数注入一个 Piece
Factory,而不是 FallingPiece
Factory!)。
现在,我至少可以看到两种方法来解决这个问题:
- 我可以为 FallingPiece 定义一个内部(C#)/包保护(Java)getter 属性,这样我就可以轻松测试它。这可能看起来无害,但我觉得它不太优雅。
- 我可以不传递一个
Piece
工厂,而是传递一个FallingPiece
工厂。然后我可以控制工厂返回哪些对象并通过它访问它们。大家觉得这样怎么样?常用吗?
还有其他方法可以解决这个问题吗?
第二个问题
与我最初将 FallingPiece 实现为不可变类型这一事实相关。这意味着,例如,每次我想要更新 FallingPiece
在 Tetris
板上的位置时,我都必须创建一个新的 FallingPiece
code> 实例和俄罗斯方块上的属性现在将指向一个新的 FallingPiece
。例如,如果我想通过传递给 Tetris
的 FallingPieceFactory
访问 FallingPiece
引用,这可能是一个大问题班级。在我看来,在尝试测试类时,如果滥用不可变数据类型可能会带来很多麻烦,对吧?或者这首先是对不可变数据类型的错误使用?
谢谢
Intro
Currently I have something of the form:
Tetris class ---> FallingPiece class ----> Piece class
A Piece
can be a Square
, a T
, etc. It has info about its shape and the shapes of its rotations, its size, etc.
The FallingPiece
class basically contains an attribute with a reference to a Piece
(the currently falling piece in the Tetris game) and will probably have its current (x, y)
location and its color.
My initial design was to have something of the form:
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);
}
...
}
}
which of course has the problem that if I later want to Unit-Test my Tetris class and to know in which (x, y)
location of the board my current FallingPiece
is, I can't. I remember seeing this "problem" exposed in the fabulous Misko Hevery's The Clean Code Talks .
The first problem
seems to be that as I'm giving the Tetris
class the responsability of creating FallingPiece
objects, I can't then have a reference to them (I have passed through constructor injection a Piece
Factory, not FallingPiece
Factory!).
Now, I can see at least 2 ways to solve this:
- I could just define an
internal
(C#)/package-protected
(Java) getter for theFallingPiece
attribute, so I can easily test it. This might seem harmless, but I don't find it too elegant. - I could instead of passing it a
Piece
Factory, pass aFallingPiece
Factory. I could then control which objects would be returned by the Factory and access them through it. What do you guys think of this way? Is it commonly used?
Is there any other way of solving this?
There is a second problem
related to fact that I've originally implemented a FallingPiece
to be an immutable type. This means that every time I want to update the FallingPiece
's position on the Tetris
board, for example, I'll have to create a new FallingPiece
instance and the attribute on Tetris will now point to a new FallingPiece
. Which can be a big problem if I want, for example, to have access to the FallingPiece
reference through a FallingPieceFactory
that is passed to the Tetris
class. It appears to me that immutable data types can be if misused a lot of headache when trying to test classes, right? Or is this a bad use of an immutable datatype, in the first place?
Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
可能的解决方案:
这样您就可以使用接口的模拟对象来测试所有内容。将 someMethodThatNeedsAFallingPiece 的可见性更改为 protected 以在测试用例中访问它或使用反射调用它(邪恶)。
这是您的单元测试:
Possiblle solution:
This way you can test everything using mock objects for your interfaces. change visibility of someMethodThatNeedsAFallingPiece to protected to access it in your test case or call it using reflection (evil).
Here's your unit test:
我只会注入一个 FallingPieceFactory 来减少俄罗斯方块与其他类的耦合。看起来它不知道
PieceFactory
和FallingPieceFactory
。我会将测试定位于俄罗斯方块类的使用。你的方法必须有明显的效果。它是如何使用的?是否有一个类使用俄罗斯方块的数据来绘制游戏领域?那么无论如何都会有一个吸气剂。或者这幅画是由
Tetris
类调用的?然后它必须引用负责的类,您可以注入并观察(可能是模拟)这个绘画类。关于不可变性:我在测试不可变对象时从未遇到过任何问题。如果 FallingPiece 是不可变的良好候选者似乎值得怀疑,因为主要目的似乎是掉落(意味着:改变其位置)。
I would inject only a
FallingPieceFactory
to reduce the coupling ofTetris
to other classes. It looks like that it havn't to know about thePieceFactory
AND theFallingPieceFactory
.I would orientate the testing on the usage of the
Tetris
class. Your method must have an observable effect. How is it used? Is there a class that uses the data ofTetris
to paint the game field? Then there would be a getter anyway. Or is the painting called by theTetris
class? Then it have to had a reference to the responsible class and you can inject and observe (possible mock) this painting class.Regarding immutibility: I never experienced any problems with testing immutable objects. If
FallingPiece
is a good candidate to be immutable seems questionable as the main purpose seems to be fall down (means: change its position).我想说的是,俄罗斯方块对象做得太多了:
这个方法有太多的责任。
fallingPiece
的逻辑。片段
。fallingPiece
。您应该遵循“最后可能的决定时刻”。摆脱这一切。 接吻。
试试这个:
将该逻辑推入
pieceModel
I would say that the
Tetris
object is doing to much:This method has too many reponsibilities.
fallingPiece
.piece
.fallingPiece
.You should be following the 'Last Possible Moment of Decision`. Get rid of all that. KISS.
Try this:
Push that logic into the
pieceModel