领先一步
VMware 提供培训和认证,以加速您的进步。
了解更多我很高兴宣布 SpringSource 的 OSGi 测试桩 1.0.0.M1 版本发布。这些桩提供了一种在不需要完整的 OSGi 容器的情况下,对复杂的 OSGi Framework 交互进行单元测试的方法。
@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 Framework 这样复杂的 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后,我们要确保一切都已清理完毕,并且系统处于干净状态。