这是关于单元测试螺栓和喷口在用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,并站起来“测试拓扑”为每个单独的Bolt
与Spout
(并验证博尔特必须提供的隐性契约,例如: -它宣称的输出)?
谢谢
我们的方法是使用一个序列化的工厂的构造函数注入喷出/螺栓。 然后喷出/螺栓咨询工厂在其开/ 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));
}
}
由于版本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
我们所采取的一种方法是将大部分的应用程序逻辑了螺栓和嘴,并到我们使用的实例,并通过最少的接口使用它们做繁重的对象。 然后,我们做这些对象和集成测试单元测试,虽然这确实留下一个缺口。
它原来是很容易模仿对象风暴像OutputDeclarer,元组和OutputFieldsDeclarer。 其中,只有永远OutputDeclarer看到任何副作用使代码OutputDeclarer模拟类能够回答任何元组和锚发出的,例如。 然后,您的测试类可以用这些模拟类的实例可以轻松地配置螺栓/壶口实例,调用它和验证预期的副作用。