领先一步
VMware 提供培训和认证,助您加速进步。
了解更多我很高兴地宣布 SpringSource OSGi Test Stubs 的 1.0.0.M1 版本发布。这些存根提供了一种在不需要完整 OSGi 容器的情况下对复杂 OSGi 框架交互进行单元测试的方法。
@Test
public void startAndStop() throws Exception {
BundleActivator bundleActivator = new DumpBundleActivator();
BundleContext context = createMock(BundleContext.class);
Filter filter = createMock(Filter.class);
String filterString = "(objectClass=" + DumpContributor.class.getName() + ")";
expect(context.createFilter(filterString)).andReturn(filter);
context.addServiceListener((ServiceListener)anyObject(), eq(filterString));
expect(context.getServiceReferences(DumpContributor.class.getName(), null)).andReturn(new ServiceReference[0]).atLeastOnce();
ServiceRegistration generatorRegistration = createMock(ServiceRegistration.class);
ServiceRegistration summaryRegistration = createMock(ServiceRegistration.class);
ServiceRegistration jmxRegistration = createMock(ServiceRegistration.class);
ServiceRegistration threadRegistration = createMock(ServiceRegistration.class);
ServiceRegistration heapRegistration = createMock(ServiceRegistration.class);
expect(context.registerService(eq(DumpGenerator.class.getName()), isA(StandardDumpGenerator.class), (Dictionary<?,?>)isNull())).andReturn(generatorRegistration);
expect(context.registerService(eq(DumpContributor.class.getName()), isA(SummaryDumpContributor.class), (Dictionary<?,?>)isNull())).andReturn(summaryRegistration);
expect(context.registerService(eq(DumpContributor.class.getName()), isA(JmxDumpContributor.class), (Dictionary<?,?>)isNull())).andReturn(jmxRegistration);
expect(context.registerService(eq(DumpContributor.class.getName()), isA(ThreadDumpContributor.class), (Dictionary<?,?>)isNull())).andReturn(threadRegistration);
expect(context.registerService(eq(DumpContributor.class.getName()), isA(HeapDumpContributor.class), (Dictionary<?,?>)isNull())).andReturn(heapRegistration);
generatorRegistration.unregister();
summaryRegistration.unregister();
jmxRegistration.unregister();
threadRegistration.unregister();
heapRegistration.unregister();
context.removeServiceListener((ServiceListener)anyObject());
replay(context, filter, generatorRegistration, summaryRegistration, jmxRegistration, threadRegistration, heapRegistration);
bundleActivator.start(context);
bundleActivator.stop(context);
verify(context, filter, generatorRegistration, summaryRegistration, jmxRegistration, threadRegistration, heapRegistration);
}
创建一套测试桩是一项精妙的平衡工作,尤其是对于 OSGi 框架这样复杂的 API 而言。一方面,你需要实现足够简单,不至于引入错误,并且允许用户指定调用的行为和返回值。另一方面,你需要足够精密的实现,以便复杂对象(例如ServiceTracker)在调用桩时能够获得预期的行为。
考虑到所有这些,我着手为BundleContext, Bundle, ServiceReference和ServiceRegistration实现了测试桩。为了让您了解这些测试桩带来的区别,下面是转换后使用这些桩的先前测试。
@Test
public void startAndStop() throws Exception {
BundleActivator bundleActivator = new DumpBundleActivator();
StubBundleContext bundleContext = new StubBundleContext().addFilter(new ObjectClassFilter(DumpContributor.class));
bundleActivator.start(bundleContext);
assertServiceListenerCount(bundleContext, 1);
assertServiceRegistrationCount(bundleContext, DumpGenerator.class, 1);
assertServiceRegistrationCount(bundleContext, DumpContributor.class, 4);
bundleActivator.stop(bundleContext);
assertCleanState(bundleContext);
}
如您所见,这个测试现在更容易阅读和维护,但最重要的是,它更容易理解。这个测试的基本构成是StubBundleContext。这个上下文被传入DumpBundleActivator的启动调用中,在此处注册服务。但真正有趣的地方在于断言。
使用StubBundleContext,用户可以断言测试所需的一切。但是,测试桩包还包含一个OSGiAssert类,它使典型的断言更具可读性。在这种情况下,您可以看到,在调用start之后,我们希望注册一个ServiceListener,一个DumpGenerator服务,以及四个DumpContributor服务。在调用stop之后,我们希望确保所有内容都已清理干净,系统处于干净状态。