测试风暴螺栓和喷口(Testing Storm Bolts and Spouts)

2019-09-03 04:29发布

这是关于单元测试螺栓和喷口在用Java编写的风暴拓扑一个一般性的问题。

什么是单元测试,推荐的做法和指南(JUnit的?) 螺栓喷口

例如,我可以写一个JUnit测试Bolt ,但没有完全理解的框架(如生命周期Bolt )和序列化的影响,容易使基于构造创造不可序列成员变量的错误。 在JUnit中,这个测试会通过,但在拓扑结构,这是行不通的。 我完全想象还有一个需要考虑(如本例中与序列化和生命周期)许多测试点。

因此,它建议,如果您使用基于JUnit的单元测试,你运行一个小的模拟拓扑结构( LocalMode ?)和测试的隐含合同Bolt (或Spout即拓扑下的)? 或者,是不是确定使用JUnit,但其含义是,我们要模拟博尔特的生命周期(创建它,称prepare()嘲讽一个Config等)谨慎? 在这种情况下,什么是类测试(螺栓/管口)一些通用的测试点来考虑的?

有什么其他的开发完成,相对于建立适当的单元测试?

我注意到有一个拓扑测试API(请参见: https://github.com/xumingming/storm-lib/blob/master/src/jvm/storm/TestingApiDemo.java )。 是它更好地使用一些API,并站起来“测试拓扑”为每个单独的BoltSpout (并验证博尔特必须提供的隐性契约,例如: -它宣称的输出)?

谢谢

Answer 1:

我们的方法是使用一个序列化的工厂的构造函数注入喷出/螺栓。 然后喷出/螺栓咨询工厂在其开/ prepare方法。 工厂的单一责任是封装在一个序列化的方式获得喷出/螺栓的依赖。 这样的设计让我们的单元测试注射假/测试/模拟工厂,,咨询时,返回模拟服务。 以这种方式,我们可以狭单元测试使用嘲笑例如所述的Mockito喷口/螺栓。

下面是一个螺栓,它测试的一般示例。 我省略了工厂实现UserNotificationFactory因为它取决于你的应用程序。 您可以使用服务定位器来获得服务,系列化配置,HDFS访问的配置,还是真的在所有得到正确的服务以任何方式,只要工厂可以在SERDE周期之后做到这一点。 你应该覆盖类的序列。

螺栓

public class NotifyUserBolt extends BaseBasicBolt {
  public static final String NAME = "NotifyUser";
  private static final String USER_ID_FIELD_NAME = "userId";

  private final UserNotifierFactory factory;
  transient private UserNotifier notifier;

  public NotifyUserBolt(UserNotifierFactory factory) {
    checkNotNull(factory);

    this.factory = factory;
  }

  @Override
  public void prepare(Map stormConf, TopologyContext context) {
    notifier = factory.createUserNotifier();
  }

  @Override
  public void execute(Tuple input, BasicOutputCollector collector) {
    // This check ensures that the time-dependency imposed by Storm has been observed
    checkState(notifier != null, "Unable to execute because user notifier is unavailable.  Was this bolt successfully prepared?");

    long userId = input.getLongByField(PreviousBolt.USER_ID_FIELD_NAME);

    notifier.notifyUser(userId);

    collector.emit(new Values(userId));
  }

  @Override
  public void declareOutputFields(OutputFieldsDeclarer declarer) {
    declarer.declare(new Fields(USER_ID_FIELD_NAME));
  }
}

测试

public class NotifyUserBoltTest {

  private NotifyUserBolt bolt;

  @Mock
  private TopologyContext topologyContext;

  @Mock
  private UserNotifier notifier;

  // This test implementation allows us to get the mock to the unit-under-test.
  private class TestFactory implements UserNotifierFactory {

    private final UserNotifier notifier;

    private TestFactory(UserNotifier notifier) {
      this.notifier = notifier;
    }

    @Override
    public UserNotifier createUserNotifier() {
      return notifier;
    }
  }

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

    // The factory will return our mock `notifier`
    bolt = new NotifyUserBolt(new TestFactory(notifier));
    // Now the bolt is holding on to our mock and is under our control!
    bolt.prepare(new Config(), topologyContext);
  }

  @Test
  public void testExecute() {
    long userId = 24;
    Tuple tuple = mock(Tuple.class);
    when(tuple.getLongByField(PreviousBolt.USER_ID_FIELD_NAME)).thenReturn(userId);
    BasicOutputCollector collector = mock(BasicOutputCollector.class);

    bolt.execute(tuple, collector);

    // Here we just verify a call on `notifier`, but we could have stubbed out behavior befor
    //  the call to execute, too.
    verify(notifier).notifyUser(userId);
    verify(collector).emit(new Values(userId));
  }
}


Answer 2:

由于版本0.8.1风暴的单元测试设施已经通过Java暴露:

  • http://storm-project.net/2012/09/06/storm081-released.html

举一个例子来使用如何API看看这里:

  • https://github.com/xumingming/storm-lib/blob/master/src/jvm/storm/TestingApiDemo.java


Answer 3:

我们所采取的一种方法是将大部分的应用程序逻辑了螺栓和嘴,并到我们使用的实例,并通过最少的接口使用它们做繁重的对象。 然后,我们做这些对象和集成测试单元测试,虽然这确实留下一个缺口。



Answer 4:

它原来是很容易模仿对象风暴像OutputDeclarer,元组和OutputFieldsDeclarer。 其中,只有永远OutputDeclarer看到任何副作用使代码OutputDeclarer模拟类能够回答任何元组和锚发出的,例如。 然后,您的测试类可以用这些模拟类的实例可以轻松地配置螺栓/壶口实例,调用它和验证预期的副作用。



文章来源: Testing Storm Bolts and Spouts