48

这是关于用 Java 编写的 Storm 拓扑中的单元测试 Bolt 和 Spout 的一般问题。

单元测试(JUnit?) BoltsSpouts的推荐实践和指南是什么?

例如,我可以为 a 编写 JUnit 测试Bolt,但在不完全理解框架(如 a 的生命周期Bolt)和序列化含义的情况下,很容易犯基于构造函数创建不可序列化成员变量的错误。在 JUnit 中,这个测试会通过,但在拓扑中,它不会工作。我完全想象有许多测试点需要考虑(例如这个带有序列化和生命周期的示例)。

因此,如果您使用基于 JUnit 的单元测试,是否建议您运行一个小型模拟拓扑( ?)并测试该拓扑下(或)LocalMode的隐含合同?或者,使用 JUnit 是否可以,但这意味着我们必须仔细模拟 Bolt 的生命周期(创建、调用、模拟 a等)?在这种情况下,被测类(Bolt/Spout)需要考虑哪些一般测试点?BoltSpoutprepare()Config

在创建适当的单元测试方面,其他开发人员做了什么?

我注意到有一个拓扑测试 API(参见:https ://github.com/xumingming/storm-lib/blob/master/src/jvm/storm/TestingApiDemo.java )。使用某些 API 并为每个人Bolt&支持“测试拓扑” Spout(并验证 Bolt 必须提供的隐式合约,例如 - 它是声明的输出)会更好吗?

谢谢

4

4 回答 4

14

自 0.8.1 版以来,Storm 的单元测试工具已通过 Java 公开:

有关如何使用此 API 的示例,请查看此处:

于 2013-10-09T13:07:32.847 回答
11

我们的方法是使用可序列化工厂的构造函数注入到 spout/bolt 中。然后喷口/螺栓在其打开/准备方法中咨询工厂。工厂的唯一职责是以可序列化的方式封装获取 spout/bolt 的依赖项。这种设计允许我们的单元测试注入假/测试/模拟工厂,当被咨询时,它们会返回模拟服务。通过这种方式,我们可以使用模拟(例如 Mockito)对 spout/bolts 进行狭隘的单元测试。

下面是一个螺栓的通用示例及其测试。我省略了工厂的实现,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));
  }
}
于 2014-11-13T23:42:19.737 回答
11

我们采用的一种方法是将大部分应用程序逻辑从 bolts 和 spout 中移到我们用来完成繁重工作的对象中,方法是通过最小的接口实例化和使用它们。然后我们对这些对象进行单元测试和集成测试,尽管这确实留下了差距。

于 2013-05-14T21:08:28.677 回答
1

It turns out to be fairly easy to mock storm objects like OutputDeclarer, Tuple and OutputFieldsDeclarer. Of those, only OutputDeclarer ever sees any side effects so code the OutputDeclarer mock class to be able to answer any tuples and anchors emitted, for example. Your test class can then use instances of those mock classes to easily configure a bolt/spout instance, invoke it and validate the expected side effects.

于 2013-06-04T15:34:09.343 回答