Spring Boot 主要范围中的 Cucumber:自动注入无法工作

4

参考这篇文章:Cucumber: no backend found when running from Spring Boot jar
我正在尝试在一个实时的Spring Boot应用程序中完整地运行Cucumber。我目前有一个POC应用程序,使用上述帖子中的代码来创建Cucumber Runtime,并运行一个引用Bean的简单Feature/Steps类:
服务:

public void testFeature(){
        RuntimeOptions runtimeOptions = new RuntimeOptions(new ArrayList<String>(asList("--glue", "org.bdd.poc", "--no-dry-run", "--monochrome", "classpath:features")));
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        ResourceLoader resourceLoader = new CustomMultiLoader(classLoader);
        ClassFinder classFinder = new ResourceLoaderClassFinder(resourceLoader, classLoader);
        Runtime runtime = new Runtime(resourceLoader, classFinder, classLoader, runtimeOptions);
        try {
            runtime.run();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

演示步骤:

(no class annotation)
public class DemoSteps {

    @Autowired
    private MyBean bean;

    public DemoSteps(){
        System.err.println("STEP INIT");
    }

    @Given("a proof of concept")
    public void givenAPOC(){

    }

    @When("doing a demo")
    public void whenDemo(){

    }

    @Then("it should talk to a bean")
    public void thenItShouldTalkToABean(){
        this.bean.poke();
    }
}

Bean:

@Component
public class MyBean {
    public void poke(){
        System.err.println("I'VE BEEN POKED! THE PAIN!");
    }
}

DemoSteps.java类有一个使用@Autowired的类字段,但它既没有填充bean引用,也没有在DemoSteps构造之后失败。调试时我看到Spring bean工厂被用于创建步骤实例,但是没有任何关于自动装配的信息。我猜想为了将主SpringContext连接到某个地方,可以采用以下两种方式之一:
  • 适当的Backend实现
  • 适当的Glue实现
我知道使用Karate框架可以实现类似的功能,但我还没有找到是什么让它能够建立连接。
当前使用的是Spring Boot 2.3.0和Cucumber 2.4.0,按照“spring-boot-maven-plugin”配置进行拆包。

我认为我要采取的路线是创建自己的cucumber.runtime.java.JavaBackend实例,并使用自定义实现的cucumber.api.java.ObjectFactory。仅从接口上看,它似乎是我与SpringContext之间的桥梁。然而,几乎可以看出,我需要创建自己的作用域来处理步骤定义,或者扩展现有的cucumber.runtime.java.spring.SpringFactory。需要进行一些实验。 - Joe Kennedy
1个回答

0
我提供的解决方案并不完全是后端,而是其中的一部分。 cucumber-spring 依赖项具有 cucumber.api.java.ObjectFactory 的实现。 这个类充当创建步骤类的访问点,这些类包含 @Given/@When/等。 有一个“测试类”来维护它。 然而,我选择了不同的路线,创建了一个“子”应用程序上下文到主要的 AnnotationApplicationContext,特别是为了处理 @Autowired 注释。 无论如何,这是代码:
服务:
Service Method:
    @Autowired(required = false)
    private List<LiveTestEventHandler> testEventHandlerList;
    @Autowired
    private ApplicationContext applicationContext;

    public void testFeature(){
        RuntimeOptions runtimeOptions = new RuntimeOptions(new ArrayList<String>(asList("--glue", "org.bddynamic.poc", "--no-dry-run", "--monochrome", "classpath:features")));
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        ResourceLoader resourceLoader = new CustomMultiLoader(classLoader);
        ClassFinder classFinder = new ResourceLoaderClassFinder(resourceLoader, classLoader);
        ObjectFactory objectFactory = new LiveTestFactory(this.applicationContext);
        JavaBackend javaBackend = new JavaBackend(objectFactory,classFinder);
        Runtime runtime = new Runtime(resourceLoader, classLoader, asList(javaBackend), runtimeOptions);
        for(LiveTestEventHandler handler : this.testEventHandlerList){
            runtime.getEventBus().registerHandlerFor(handler.getSupportedClass(),handler);
        }
        try {
            runtime.run();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

自定义工厂。

这是cucumber-spring:runtime.java.spring.SpringFactory.java的精简版

public class LiveTestFactory implements ObjectFactory {
    private final DefaultListableBeanFactory beanFactory;
    private final GenericApplicationContext applicationContext;
    private final Collection<Class<?>> stepClasses = new HashSet();
    public LiveTestFactory(ApplicationContext applicationContext){
        AnnotationConfigApplicationContext childContext = new AnnotationConfigApplicationContext();
        childContext.setParent(applicationContext);
        this.applicationContext = childContext;
        this.beanFactory = (DefaultListableBeanFactory) this.applicationContext.getBeanFactory();
    }
    @Override
    public void start() {
        ((ConfigurableApplicationContext)this.applicationContext).registerShutdownHook();
        beanFactory.registerScope(LiveTestScope.NAME, new LiveTestScope());
        for(Class stepClass : this.stepClasses){
            this.registerStepClassBeanDefinition(this.beanFactory, stepClass);
        }
        this.applicationContext.refresh();
        this.applicationContext.start();
    }
    @Override
    public void stop() {
        LiveCodeContext.getInstance().stop();
        this.applicationContext.stop();
    }
    @Override
    public boolean addClass(Class<?> stepClass) {
        if (!this.stepClasses.contains(stepClass)) {
            this.stepClasses.add(stepClass);
        }
        return true;
    }
    @Override
    public <T> T getInstance(Class<T> type) {
        try {
            return this.applicationContext.getBean(type);
        } catch (BeansException var3) {
            throw new CucumberException(var3.getMessage(), var3);
        }
    }
    private void registerStepClassBeanDefinition(DefaultListableBeanFactory beanFactory, Class<?> stepClass) {
        BeanDefinitionRegistry registry = beanFactory;
        BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(stepClass).setScope(LiveTestScope.NAME).getBeanDefinition();
        registry.registerBeanDefinition(stepClass.getName(), beanDefinition);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接