mockito_Mockito和Hamcrest的试驾制造商

mockito

过去,很多人问我是否测试吸气剂和吸气剂(属性,属性等)。 他们还问我是否测试我的建筑商。 在我看来,答案取决于情况。

当使用遗留代码时,我不会费心去测试数据结构,这意味着对象只带有getter和setter,映射,列表等。原因之一是我从不模拟它们。 在测试使用它们的类时,我照原样使用它们。 对于构建器,当它们仅由测试类使用时,我也不会对其进行单元测试,因为它们在许多其他测试中均被用作“帮助者”。 如果它们有错误,则测试将失败。 总而言之,如果这些数据结构和构建器已经存在,那么我不会为它们进行改装测试。

但是,现在让我们谈谈进行TDD并假设您需要一个带有getter和setter的新对象。 在这种情况下,是的,我将为吸气剂和吸气剂编写测试,因为我需要先编写测试来证明它们的存在。
为了拥有丰富的领域模型,我通常倾向于将业务逻辑与数据相关联,并拥有更丰富的领域模型。 让我们看下面的例子。

在现实生活中,我会一次编写测试,使它们通过并重构。 在这篇文章中,为清晰起见,我仅向您提供完整的课程。 首先让我们编写测试:

package org.craftedsw.testingbuilders;
 
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertThat;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
 
 @RunWith(MockitoJUnitRunner.class)
 public class TradeTest {
 
  private static final String INBOUND_XML_MESSAGE = '<message >';
  private static final boolean REPORTABILITY_RESULT = true;
  private Trade trade;
 
  @Mock private ReportabilityDecision reportabilityDecision;
 
  @Before
  public void initialise() {
   trade = new Trade();
   when(reportabilityDecision.isReportable(anyString()))
     .thenReturn(REPORTABILITY_RESULT);
  }
 
  @Test public void
  should_contain_the_inbound_xml_message() {
   trade.setInboundMessage(INBOUND_XML_MESSAGE);
 
   assertThat(trade.getInboundMessage(), is(INBOUND_XML_MESSAGE));
  }
 
  @Test public void
  should_tell_if_it_is_reportable() {
   trade.setInboundMessage(INBOUND_XML_MESSAGE);
   trade.setReportabilityDecision(reportabilityDecision);
 
   boolean reportable = trade.isReportable();
 
   verify(reportabilityDecision).isReportable(INBOUND_XML_MESSAGE);
   assertThat(reportable, is(REPORTABILITY_RESULT));
  }
 
 }

现在执行:

package org.craftedsw.testingbuilders;
 
 public class Trade {
 
  private String inboundMessage;
  private ReportabilityDecision reportabilityDecision;
 
  public String getInboundMessage() {
   return this.inboundMessage;
  }
 
  public void setInboundMessage(String inboundXmlMessage) {
   this.inboundMessage = inboundXmlMessage;
  }
 
  public boolean isReportable() {
   return reportabilityDecision.isReportable(inboundMessage);
  }
 
  public void setReportabilityDecision(ReportabilityDecision reportabilityDecision) {
   this.reportabilityDecision = reportabilityDecision;
  }
 
 }

这种情况很有趣,因为Trade对象具有一个名为inboundMessage的属性,具有相应的getter和setter,并且在isReportable业务方法中还使用了一个协作者(reportabilityDecision,通过setter注入)。

我多次见过的“测试” setReportabilityDecision方法的常见方法是引入getReportabilityDecision方法,该方法返回reportabilityDecision(协作者)对象。

这绝对是错误的方法。 我们的目标应该是测试协作器的使用方式,即是否使用正确的参数调用协作器,以及是否使用返回的任何东西(如果返回任何东西)。 在这种情况下引入吸气剂是没有意义的,因为它不能保证在通过设置器注入了协作者之后,对象将按照我们的预期与协作者进行交互。

顺便说一句,当我们编写有关将如何使用协作者的测试时,定义它们的接口是在将TDD用作设计工具而不仅仅是将其用作测试工具时。 我将在以后的博客文章中进行介绍。

好的,现在假设可以以不同的方式(即具有不同的可报告性决策)创建此贸易对象。 我们还希望使代码更具可读性,并决定为Trade对象编写一个生成器。 在这种情况下,我们还假设我们希望生成器也用于生产和测试代码中。 在这种情况下,我们要测试驱动器。

这是我通常在开发人员测试驱动构建器实现时发现的一个示例。

package org.craftedsw.testingbuilders;
 
 import static org.craftedsw.testingbuilders.TradeBuilder.aTrade;
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertThat;
 import static org.mockito.Mockito.verify;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
 
 @RunWith(MockitoJUnitRunner.class)
 public class TradeBuilderTest {
 
  private static final String TRADE_XML_MESSAGE = '<message >';
 
  @Mock
  private ReportabilityDecision reportabilityDecision;
 
  @Test public void
  should_create_a_trade_with_inbound_message() {
   Trade trade = aTrade()
         .withInboundMessage(TRADE_XML_MESSAGE)
         .build();
 
   assertThat(trade.getInboundMessage(), is(TRADE_XML_MESSAGE));
  }
 
  @Test public void
  should_create_a_trade_with_a_reportability_decision() {
   Trade trade = aTrade()
         .withInboundMessage(TRADE_XML_MESSAGE)
         .withReportabilityDecision(reportabilityDecision)
         .build();
 
   trade.isReportable();
 
   verify(reportabilityDecision).isReportable(TRADE_XML_MESSAGE);
  }
 
 }

现在让我们看看这些测试。 好消息是,测试以开发人员希望阅读的方式编写。 这也意味着他们正在“设计” TradeBuilder公共接口(公共方法)。 坏消息是他们如何测试它。

如果仔细看,构建器的测试与TradeTest类中的测试几乎相同。
您可能会说没问题,因为构建器正在创建对象,并且测试应该相似。 唯一的不同是,在TradeTest中我们手动实例化对象,在TradeBuilderTest中我们使用构建器实例化对象,但是断言应该相同,对吗?
对我来说,首先我们要重复。 其次,TradeBuilderTest没有显示出它的真实意图。 经过多次重构和探索不同的想法之后,在与团队中的一个人进行配对编程时,我们想到了这种方法:

package org.craftedsw.testingbuilders;
 
 import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.verify;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.Spy;
 import org.mockito.runners.MockitoJUnitRunner;
 
 @RunWith(MockitoJUnitRunner.class)
 public class TradeBuilderTest {
 
  private static final String TRADE_XML_MESSAGE = '<message >';
 
  @Mock private ReportabilityDecision reportabilityDecision;
  @Mock private Trade trade;
 
  @Spy @InjectMocks TradeBuilder tradeBuilder;
 
  @Test public void
  should_create_a_trade_with_all_specified_attributes() {
   given(tradeBuilder.createTrade()).willReturn(trade);
 
   tradeBuilder
    .withInboundMessage(TRADE_XML_MESSAGE)
    .withReportabilityDecision(reportabilityDecision)
    .build();
 
   verify(trade).setInboundMessage(TRADE_XML_MESSAGE);
   verify(trade).setReportabilityDecision(reportabilityDecision);
  }
 
 }

因此,现在,TradeBuilderTest表达了TradeBuilder的期望,即调用build方法时的副作用。 我们希望它创建交易并设置其属性。 TradeTest没有重复项。 它留给TradeTest来保证Trade对象的正确行为。
为了完善起见,这是最后的TradeBuider类:

package org.craftedsw.testingbuilders;
 
 public class TradeBuilder {
 
  private String inboundMessage;
  private ReportabilityDecision reportabilityDecision;
 
  public static TradeBuilder aTrade() {
   return new TradeBuilder();
  }
 
  public TradeBuilder withInboundMessage(String inboundMessage) {
   this.inboundMessage = inboundMessage;
   return this;
  }
 
  public TradeBuilder withReportabilityDecision(ReportabilityDecision reportabilityDecision) {
   this.reportabilityDecision = reportabilityDecision;
   return this;
  }
 
  public Trade build() {
   Trade trade = createTrade();
   trade.setInboundMessage(inboundMessage);
   trade.setReportabilityDecision(reportabilityDecision);
   return trade;
  }
 
  Trade createTrade() {
   return new Trade();
  }
 
 }

MockitoHamcrest的结合非常强大,使我们能够编写更好,更易读的测试。

参考:来自Crafts Software博客的JCG合作伙伴 Sandro Mancuso的Mockito和Hamcrest的测试驱动构建器


翻译自: https://www.javacodegeeks.com/2012/06/test-driving-builders-with-mockito-and.html

mockito

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值