【问题标题】:Mock out class nested inside classes for testing模拟嵌套在类中以进行测试的类
【发布时间】:2016-04-14 05:36:45
【问题描述】:

Alpha - 以 beta 作为子代的父代

public class Alpha {
  Beta beta;

  public Alpha(int argument) {}

  void start() {
    beta = createBeta();
  }

  Beta createBeta() {
    return new Beta(this);
  }
}

Beta - alpha 的孩子,拥有 charlie

public class Beta {
  Alpha alpha;
  Charlie charlie;

  public Beta(Alpha alpha) {
    this.alpha = alpha;
    this.charlie = createCharlie();
  }

  Charlie createCharlie() {
    return new Charlie().shuffle();
  }
}

Charlie - 有一个列表,通常在生产中被打乱

public class Charlie {
  List<Integer> list = new ArrayList<Integer>();

  public Charlie() {
    for (int i = 0; i < 6; i++) {
      list.add(i);
    }
  }

  public Charlie shuffle() {
    Collections.shuffle(list);
    return this;
  }

  @Override
  public String toString() {
    return list.toString();
  }
}

AlphaTest [需要有关此测试的帮助] - 想尝试不同的改组变体,看看 alpha/beta 会如何反应。

public class AlphaTest {

  Charlie special = new Charlie();

  @Test
  public void testSpecialCharlie() {
    Alpha alpha = Mockito.spy(new Alpha(0));
    Beta beta = Mockito.spy(alpha.createBeta());

    Mockito.when(alpha.createBeta()).thenReturn(beta);
    Mockito.when(beta.createCharlie()).thenReturn(special);

    alpha.start();

    // FAILURE: expected:<[0, 1, 2, 3, 4, 5]> but was:<[0, 4, 1, 5, 3, 2]>
    assertEquals(special.list, alpha.beta.charlie.list);
  }
}

目标是用查理的不同组合测试 Alpha/Beta。不确定最好的方法是什么?这是复制的确切代码。也可以更改设置以方便测试。尝试了不同的变化,没有任何真正的工作。非常感谢您的帮助。

我不确定,我尝试了很多方法(模拟 createCharlie() 函数,模拟 Charlie 类,模拟 shuffle(),将 createCharlie() 移动到父 Alpha 类,没有什么能正常工作或者我可能遗漏了什么。任何帮助将不胜感激。谢谢!

想知道为什么我不能这样做:

Charlie charlie = Mockito.mock(Charlie.class);
Mockito.when(charlie.shuffle()).thenReturn(special);

【问题讨论】:

  • “没有什么能真正正常工作”是什么意思?
  • 对不起,我的意思是,我无法让它工作,我对测试真的很陌生,所以我可能做得不对。我也可以稍微调整一下设置。请让我知道你将如何做到这一点?谢谢!
  • 这样的单元测试代码使用 JMockit 非常容易(您只需声明一个 @Mocked Beta@Mocked Charlie 字段/参数,然后记录/验证任何调用,无论实例如何在 CUT 中创建)... 但是,我首先会问为什么要模拟这里的任何东西?请记住,嘲笑是不能被滥用的。像 Kent Beck(TDD 的创建者)、Martin Fowler 和其他“大师”这样的人不喜欢嘲笑(有充分的理由)。
  • 我实际上不知道去这里的最佳方式是什么。我没有设置 JMockit。认为这应该是直截了当的,测试经验有限,但已经浪费了几天,仍然无法弄清楚:(

标签: java unit-testing testing mocking mockito


【解决方案1】:

如果有人觉得这很有用,我会在下面留下我最初的答案。

首先,将 Beta 放入构造函数的最后一行:

System.out.println("Charlie created: " + this.charlie.hashCode());

运行测试后你会看到 charlie 被创建了多次。

首先在测试中调用它时:

Beta beta = Mockito.spy(alpha.createBeta());

以及稍后调用它时:

Mockito.when(alpha.createBeta()).thenReturn(beta);

所以 Beta 一直引用真实的 Charlie,并且因为您在调用代码时模拟了 createCharlie()

Assert.assertEquals(special.list, alpha.beta.charlie.list);

调用真正的Charlie,而不是嘲笑一个。您实现课程的方式并不是很好,但如果我只是回答您应该在测试中更改的内容 - 您应该改为:

Assert.assertEquals(special.list, alpha.beta.createCharlie().list);

在这种情况下,模拟的Charlie 将被调用并且测试将通过。


上一个答案:

通常,当您对某些东西进行单元测试时,您的重点是单个测试类。你可以使用@Spy/@InjectMocks。当你测试Beta时,你应该模拟Charlie,当你测试Alpha时,你将模拟Beta...

我不知道目的,但您的代码中奇怪的是 createCharlie()randomize() 返回 Charlie 的实例。无论如何,您可以通过 getter 懒惰地创建 Charlie,如下所示:

public class Beta {
    private Charlie charlie;

    public Beta() {
    }

    public Charlie getCharlie() {
        if (charlie == null) {
            charlie = new Charlie();
        }
        return charlie;
    }

    public void doSomethingWithCharlie() {
        getCharlie().randomize();
    }
}

你可以像这样在你的测试中注入 charlie:

public class BetaTest{
    @Mock(name="charlie") // variable name in Beta
    private Charlie charlieMock;

    @InjectMocks
    private Beta beta;

    @BeforeMethod
    public void before() {
        MockitoAnnotations.initMocks(this);
    }

    public void test1(){
        beta.doSomethingWithCharlie();
    }
}

另请参阅@InjectMocks 文档。

您可以选择将包保护/公共方法添加到Alpha

Beta createBeta() {
    return new Beta(); 
}

Beta getBeta() {
    if(beta==null){
        beta = new Beta();
    }

    return beta;
}

然后你可以注入 Alpha 真正的 Beta ,它返回模拟的Charlie

公共类 AlphaTest { @嘲笑 查理·查理莫克;

@Spy
Beta beta;

@Spy
Alpha alpha;

public void beforeTest() {
    when(alpha.getBeta()).thenReturn(beta);
    when(beta.getCharlie()).thenReturn(charlie);
}

}

另见this

【讨论】:

  • 谢谢达里奥,也许我不清楚,我在我的问题中添加了更多信息。我同意测试单个类,但不幸的是,在这种情况下,Beta 依赖于 Alpha,我无法将它们解耦。目标是验证与不同 Charlie 的 Alpha/Beta 交互,这就是为什么我想模拟 charlie 的创建,这样我就可以针对不同的 Charlie 实例编写测试。希望澄清一点?
  • 我还在回复中附加了其他信息。
  • 我尝试了第二种方法,但没有奏效:( - 我不确定我错过了什么,这也是我之前尝试过的。
  • 使用调试并验证模拟被注入
  • 嗨达里奥,请再看看这个问题。那是复制的确切代码,因此您确切知道我要做什么。非常感谢你抽出时间来做这件事。非常期待您的下一次回复。
【解决方案2】:

感谢达里奥的回答,我把 Beta 类改成了

public class Beta {
  Alpha alpha;
  Charlie _charlie;

  public Beta(Alpha alpha) {
    this.alpha = alpha;
    this._charlie = getCharlie();
    // PROBLEM: If I use this._charlie here, that will use the wrong charlie!
  }

  Charlie getCharlie() {
    if (_charlie == null) {
      _charlie = new Charlie().shuffle();
    }
    return _charlie;
  }
}

然后,我从不使用 alpha.beta._charlie 访问 charlie,总是使用 getCharlie(),这应该可以解决我的问题。谢谢达里奥,非常感谢!

【讨论】:

  • 添加了有关此方法问题的评论。我真正需要的是设置我的代码,以便第一个 getCharlie() 调用返回 special 实例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-23
  • 1970-01-01
相关资源
最近更新 更多