c# - 关于在将进行单元测试的类中使用 new 运算符的问题

标签 c# java unit-testing oop immutability

简介

目前我有以下形式的内容:

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!)。

现在,我至少可以看到两种方法来解决这个问题:

  1. 我可以定义一个 internal (C#)/package-protected (Java) FallingPiece 的 getter属性,这样我就可以轻松测试它。这可能看起来无害,但我觉得它不太优雅。
  2. 我可以不传递它 Piece工厂,通证FallingPiece工厂。然后我可以控制工厂返回哪些对象并通过它访问它们。大家觉得这样怎么样?常用吗?

还有其他方法可以解决这个问题吗?

还有第二个问题

与我最初实现了 FallingPiece 相关的事实成为不可变类型。这意味着每次我想更新FallingPieceTetris 上的位置例如,我必须创建一个新的 FallingPiece俄罗斯方 block 上的实例和属性现在将指向新的 FallingPiece 。例如,如果我想访问FallingPiece,这可能是一个大问题。通过FallingPieceFactory引用被传递到Tetris类(class)。在我看来,在尝试测试类时,如果滥用不可变数据类型可能会带来很多麻烦,对吧?或者这首先是对不可变数据类型的错误使用?

谢谢

最佳答案

可能的解决方案:

  1. 不要将实现连接在一起,而是使用接口(interface)。
  2. 引入一个生成器来创建 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/

相关文章:

c# - 保护业务对象免受更改

java - 如何使用 Java 中的流将没有泛型的列表转换为带有泛型的列表?

java - Maven - 始终下载最新版本的依赖项或插件

c# - 是否可以将方法限制为仅在友好程序集中使用虚拟方法?

c# - 如何使用 Javascript 在 GridView 中查找文本框

C# Entity Framework IQueryable 内存泄漏

c# - LINQ GroupBy 不对结果进行分组

java - 获取映射通过引用应用程序启动时的未知目标实体属性异常

java - 文档中哪里提到了 OpenCMIS 单元测试?

c++ - 如何在进行 GoogleTest 时跳过源代码中的部分“代码”