测试风暴螺栓和喷口

swvgeqrz  于 2021-06-24  发布在  Storm
关注(0)|答案(4)|浏览(396)

这是一个关于在用java编写的storm拓扑中对螺栓和喷口进行单元测试的一般性问题。
单元测试(junit?)螺栓和喷嘴的推荐实践和指南是什么?
例如,我可以为 Bolt ,但是没有完全理解框架(比如 Bolt )而序列化的含义,很容易犯基于构造函数创建不可序列化成员变量的错误。在junit中,这个测试可以通过,但是在拓扑中,它不起作用。我完全可以想象有许多需要考虑的测试点(比如这个带有序列化和生命周期的示例)。
因此,如果您使用基于junit的单元测试,建议您运行一个小的模拟拓扑( LocalMode ?)并测试 Bolt (或 Spout )在那个拓扑下?或者,使用junit可以吗,但这意味着我们必须模拟bolt的生命周期(创建它,调用 prepare() ,嘲笑 Config 等)小心?在这种情况下,对于被测试的类(螺栓/喷口),需要考虑哪些一般测试点?
其他开发人员在创建适当的单元测试方面做了什么?
我注意到有一个拓扑测试api(请参阅:https://github.com/xumingming/storm-lib/blob/master/src/jvm/storm/testingapidemo.java). 使用一些api,并为每个人建立“测试拓扑”是否更好 Bolt & Spout (以及验证螺栓必须提供的隐式契约,例如-它的声明输出)?
谢谢

zvms9eto

zvms9eto1#

我们采用的一种方法是将大多数应用程序逻辑从螺栓和管口中移开,并通过最少的接口示例化和使用它们,将它们移入我们用来完成繁重工作的对象中。然后我们对这些对象进行单元测试和集成测试,尽管这确实留下了一个空白。

blpfk2vs

blpfk2vs2#

我们的方法是将可序列化工厂的构造函数注入到喷口/螺栓中。然后,喷口/螺栓以其打开/准备方法咨询工厂。工厂的单一职责是以可串行化的方式封装获取喷口/螺栓的依赖关系。这种设计允许我们的单元测试注入假/测试/模拟工厂,这些工厂在咨询时返回模拟服务。通过这种方式,我们可以使用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));
  }
}
krcsximq

krcsximq3#

由于版本0.8.1 storm的单元测试设施已通过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

ldioqlga

ldioqlga4#

事实证明,模拟outputdeclarer、tuple和outputfieldsdeclarer等风暴对象相当容易。其中,只有outputdeclarer会看到任何副作用,因此对outputdeclarer模拟类进行编码,以便能够回答发出的任何元组和锚定。然后,您的测试类可以使用这些模拟类的示例来轻松地配置bolt/spout示例,调用它并验证预期的副作用。

相关问题