黄瓜与Guice - 多个guice注射器 [英] Cucumber with Guice - multiple guice injector

查看:314
本文介绍了黄瓜与Guice - 多个guice注射器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 Cucumber Guice 作为DI。
我遇到以下问题:
我有一个步骤,即

  class MyStep {

@Inject
private MyService myService;

@Given(Some acction happen)
public void sthHappen(){
myService.doSth();
}
}

我有这个类运行它 JUnit 测试

  @RunWith(Cucumber.class)
@CucumberOptions(...)
public class MyTest {

}

有一个

  class MyModule extends AbstractModule {
@Override
protected void configure {
bind(MyService.class).to(MyFirstService.class);
}
}



< MyInjectorSource
我定义cucumber.properties其中我定义 guice.injector-source = MyInjectorSource;
文件与方案。
一切正常工作。



没有我想运行MyStep步骤与其他MyService实现(当然我不会重复的代码MyStep)
我使用新的场景定义了一个新的特性文件,并且新的测试类

  @RunWith(Cucumber.class )
@CucumberOptions(...)
public class MyOtherTest {

}

现在我试图创建另一个 InjectorSource ,但是我不能配置它。

createRuntime 方法。



最新的cucumber-guice 1.2.5使用几个阶段来创建注入器,不幸的是它使用全局变量 cucumber.runtime.Env.INSTANCE 。此变量由 cucumber.properties System.getProperties 填充。



流程是:




  • Cucumber运行器扫描可用的后端(在我的设置中它是 cucumber.runtime。 java.JavaBackend

  • 一个JavaBackend构造函数加载 ObjectFactory (在我的设置中是黄瓜。 runtime.java.guice.impl.GuiceFactory)

  • GuiceFactory通过InjectorSourceFactory检查 Env.INSTANCE ,它将创建自定义 InjectorSource 或默认注入器



理想情况下,黄瓜应将其在启动时创建的RuntimeOptions InjectorSource但不幸的是它不使用全局变量。这不是容易创建补丁像这样,所以我的解决方案简化了这种方法,并通过阅读新的注释直接创建InjectorSource在定制跑步者。

  public class GuiceCucumberRunner extends Cucumber {

public GuiceCucumberRunner(Class<?> clazz)throws InitializationError ,IOException {
super(clazz);
}

@Override
protected Runtime createRuntime(ResourceLoader resourceLoader,ClassLoader classLoader,RuntimeOptions runtimeOptions)throws InitializationError,IOException {
运行时结果= new Runtime(resourceLoader,classLoader ,Arrays.asList(createGuiceBackend()),runtimeOptions);
return result;
}

private JavaBackend createGuiceBackend(){
GuiceCucumberOptions guiceCucumberOptions = getGuiceCucumberOptions();
InjectorSource injectorSource = createInjectorSource(guiceCucumberOptions.injectorSource());
ObjectFactory objectFactory = new GuiceFactory(injectorSource.getInjector());
JavaBackend result = new JavaBackend(objectFactory);
return result;
}

private GuiceCucumberOptions getGuiceCucumberOptions(){
GuiceCucumberOptions guiceCucumberOptions = getTestClass()。getJavaClass()。getAnnotation(GuiceCucumberOptions.class);
if(guiceCucumberOptions == null){
String message = format(Suite class{0}is missing annotation GuiceCucumberOptions,getTestClass()。getJavaClass());
throw new CucumberException(message);
}
return guiceCucumberOptions;
}

private InjectorSource createInjectorSource(Class< ;? extends InjectorSource> clazz){
try {
return clazz.newInstance();
} catch(Exception e){
String message = format('{0}的实例化失败,InjectorSource必须有一个公有的零args构造函数。
throw new InjectorSourceInstantiationFailed(message,e);
}
}

静态类GuiceFactory实现ObjectFactory {

private final Injector injector;

GuiceFactory(Injector injector){
this.injector = injector;
}

@Override
public boolean addClass(Class<?> clazz){
return true;
}

@Override
public void start(){
injector.getInstance(ScenarioScope.class).enterScope();
}

@Override
public void stop(){
injector.getInstance(ScenarioScope.class).exitScope();
}

@Override
public< T> T getInstance(Class< T> clazz){
return injector.getInstance(clazz);
}
}
}

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface GuiceCucumberOptions {

Class< ;? extends InjectorSource> injectorSource();

}

@RunWith(GuiceCucumberRunner.class)
@GuiceCucumberOptions(injectorSource = MyInjector.class)
@CucumberOptions(
.. 。

public class Suite {

}

我需要复制GuiceFactory,因为它不暴露正常构造函数(!)


I'm using Cucumber with Guice as DI. I've encountered following problem: I've got one step i.e.

class MyStep() {

    @Inject
    private MyService myService;

    @Given("Some acction happen")
    public void sthHappen() {
        myService.doSth();
    }
}

And I've got this class to run it as JUnit test

@RunWith(Cucumber.class)
@CucumberOptions(...)
public class MyTest {

}

There is a

class MyModule extends AbstractModule {
    @Override
    protected void configure() {
         bind(MyService.class).to(MyFirstService.class);     
    }
}

which is used by my MyInjectorSource I define cucumber.properties where I define guice.injector-source=MyInjectorSource; There is also a feature file with scenario. Everything is working for now.

And no i would like to run MyStep step with other MyService implementation (of course I don't wont to duplicate code of MyStep) I define a new feature file with new scenarios, and new Test class

@RunWith(Cucumber.class)
@CucumberOptions(...)
public class MyOtherTest {

}

And now I've tried to create another InjectorSource but I was not able to configure it.

解决方案

Solution which I've found is using custom Junit4 runner inheriting from original Cucumber runner and changing its createRuntime method.

Latest cucumber-guice 1.2.5 uses few stages to create injector and unfortunately it uses global variable cucumber.runtime.Env.INSTANCE. This variable is populated from cucumber.properties and System.getProperties.

Flow is:

  • Cucumber runner scans available backends (in my setup it is cucumber.runtime.java.JavaBackend)
  • One of JavaBackend constructor loads available ObjectFactory (in my setup it is cucumber.runtime.java.guice.impl.GuiceFactory)
  • GuiceFactory via InjectorSourceFactory checks Env.INSTANCE, it will create custom InjectorSource or default injector

Ideally cucumber should pass its 'RuntimeOptions` created at start to backend and InjectorSource but unfortunately it doesn't and uses global variable. It is not easy create patch like this one so my solution simplifies this approach and directly create InjectorSource in custom runner by reading new annotation.

public class GuiceCucumberRunner extends Cucumber {

    public GuiceCucumberRunner(Class<?> clazz) throws InitializationError, IOException {
        super(clazz);
    }

    @Override
    protected Runtime createRuntime(ResourceLoader resourceLoader, ClassLoader classLoader, RuntimeOptions runtimeOptions) throws InitializationError, IOException {
        Runtime result = new Runtime(resourceLoader, classLoader, Arrays.asList(createGuiceBackend()), runtimeOptions);
        return result;
    }

    private JavaBackend createGuiceBackend() {
        GuiceCucumberOptions guiceCucumberOptions = getGuiceCucumberOptions(); 
        InjectorSource injectorSource = createInjectorSource(guiceCucumberOptions.injectorSource());
        ObjectFactory objectFactory = new GuiceFactory(injectorSource.getInjector());
        JavaBackend result = new JavaBackend(objectFactory);
        return result;
    }

    private GuiceCucumberOptions getGuiceCucumberOptions() {
        GuiceCucumberOptions guiceCucumberOptions = getTestClass().getJavaClass().getAnnotation(GuiceCucumberOptions.class);
        if (guiceCucumberOptions == null) {
            String message = format("Suite class ''{0}'' is missing annotation GuiceCucumberOptions", getTestClass().getJavaClass());
            throw new CucumberException(message);
        }
        return guiceCucumberOptions;
    }

    private InjectorSource createInjectorSource(Class<? extends InjectorSource> clazz) {
        try {
            return clazz.newInstance();
        } catch (Exception e) {
            String message = format("Instantiation of ''{0}'' failed. InjectorSource must have has a public zero args constructor.", clazz);
            throw new InjectorSourceInstantiationFailed(message, e);
        }
    }

    static class GuiceFactory implements ObjectFactory {

        private final Injector injector;

        GuiceFactory(Injector injector) {
            this.injector = injector;
        }

        @Override
        public boolean addClass(Class<?> clazz) {
            return true;
        }

        @Override
        public void start() {
            injector.getInstance(ScenarioScope.class).enterScope();
        }

        @Override
        public void stop() {
            injector.getInstance(ScenarioScope.class).exitScope();
        }

        @Override
        public <T> T getInstance(Class<T> clazz) {
            return injector.getInstance(clazz);
        }
    }
}

 @Retention(RetentionPolicy.RUNTIME)
 @Target({ ElementType.TYPE })
 public @interface GuiceCucumberOptions {

     Class<? extends InjectorSource> injectorSource();

 }

@RunWith(GuiceCucumberRunner.class)
@GuiceCucumberOptions(injectorSource = MyInjector.class)
@CucumberOptions(
        ...
)
public class Suite {

}

I needed to copy GuiceFactory because it doesn't exposes normal constructor (!)

这篇关于黄瓜与Guice - 多个guice注射器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆