OSGi在集成测试中的应用

最近对测试(集成测试+单元测试)代码进行了一下重构, 导致很多以前运行通过的测试现在都无法通过了, 由于我们的test case在测试前需要加载一堆spring配置文件, 而这个加载过程耗尽了99%的测试时间, 无尽的等待实在让人心烦. 虽然使用TestSuite可以对这些spring配置文件只需要加载一次, 但是如果需要对一个个的testcase进行单个调试修改的话, 就比较耗时了.
当然我们的测试也需要调整, 现在的spring配置文件耦合还是比较紧, 导致某一个测试需要依赖一些无关的bean, 虽然我们采用了spring bean的延迟加载策略, 但是依然是杯水车薪, 效果不是很明显.
好吧, 转入正题, 这里我们需要利用OSGi的动态加载和热替换特性, 将test case与spring container采用bundle的组织形式进行分离. 然后测试和spring container在OSGi容器中运行. 这样当我们需要对单个test case运行的时候就不再需要加载spring container了, 直接使用bundle中的spring container了.除非对spring container中的bean进行了修改需要重启加载spring container之外, 其他时候则不需要重启之. 对test case也采用同样的处理方式, 这样可以采用边改边测. 大幅度的提高了修改test case的效率.

下面说说大致做法
首先将spring 配置相关的东东使用一个bundle封装起来, 并对外提供一个类似这样的暴露接口:
public interface SpringContext {
    public ApplicationContext getApplicationContext();
}

并实现一个对外提供服务的Activator:
public class SpringContextActivator implements BundleActivator {
    private ServiceRegistration sf;

    public void start(final BundleContext context) throws Exception {
        sf = context.registerService(SpringContext.class.getName(), new SpringContextImpl(), null);
    }
    public void stop(BundleContext context) throws Exception {
        sf.unregister();
    }
}

然后将test case用另外一个bundle进行封装, 也就是将原有的工程转换成plug-in工程. 这里我们采用的测试框架是Unitils 3.1, 需要对SpringModule进行重载, 改写其中创建ApplicationContext的过程:
public class OsgiSpringModule extends SpringModule {
    @Override
    public boolean isApplicationContextConfiguredFor(Object testObject) {
        return true;
    }
    @Override
    public void invalidateApplicationContext(Class<?>... classes) {
    }
    @Override
    public void init(Properties configuration) {
    }
    @Override
    public ApplicationContext getApplicationContext(Object testObject) {
        return SpringContextHolder.get();
    }
}

然后提供一个执行指定测试类的Activator:
public class TestActivator implements BundleActivator {
    private static final Log logger = LogFactory.getLog(TestActivator.class);
    private ServiceReference sr;
    public void start(BundleContext context) throws Exception {
        sr = context.getServiceReference(SpringContext.class.getName());
        SpringContext ctx = (SpringContext) context.getService(sr);
        SprongContextHolder.set(ctx.getApplicationContext());
        //执行指定测试类或方法...
   }
    public void stop(BundleContext context) throws Exception {
        SpringContextHolder.remove();
    }
}

怎么通过代码方式来执行指定的类和方法, 这里我有写, 可以参考下.
同时使用了一个SpringContextHolder, 用来在Activator和SpringModule之间传递ApplicationContext:
public class SpringContextHolder {
    public static ThreadLocal<ApplicationContext> inner = new ThreadLocal<ApplicationContext>();

    public static ApplicationContext get() {
        return inner.get();
    }

    public static void set(ApplicationContext value) {
        inner.set(value);
    }

    public static void remove() {
        inner.remove();
    }
}

另外使用到了DataSet, 而里面的测试数据的路径也需要改写, 以前采用是与测试文件同路径, 当采用OSGi之后, 路径全变了, 采用的是bundleresources协议的URL, 而原有test采用的是file协议的URL, 因此需要在二者之间进行一个转换.具体做法我已经在 OSGi小结里面进行了说明.

这些就是大致的做法, 里面还有一些细节需要注意, 否则很难跑起来:(

你可能感兴趣的:(spring,框架,bean,单元测试,osgi)