空手道:如何使用YAML文件而不是application.properties配置空手道模拟servlet?

时间:2019-04-23 19:27:09

标签: spring-boot yaml config karate

我们有一个Spring Boot项目,该项目使用application.yml文件为数据源,rabbitmq等内容配置spring上下文。

可以将YAML文件加载到karate-config.js中以获取baseUrl,并且工作正常。

function fn() {    
   var env = karate.env; // get system property 'karate.env'
   karate.log('karate.env system property was:', env);
   if (!env) {
      env = 'dev';
   }
   karate.log('karate environment set to:', env);

   var config = karate.read('classpath:application.yml');
   karate.log('baseUrl configured for API tests: '+config.baseUrl)

   if (env == 'dev') {
      var Factory = Java.type('MockSpringMvcServlet');
      karate.configure('httpClientInstance', Factory.getMock());
      //var result = karate.callSingle('classpath:demo/headers/common-noheaders.feature', config);
   } else if (env == 'stg') {
      // customize
   } else if (env == 'prod') {
      // customize
   }

   return config;
}

从空手道模拟servlet演示github项目中获取的Mock Spring MVC servlet类:

/**
 * @author pthomas3
 */
public class MockSpringMvcServlet extends MockHttpClient {

    private final Servlet servlet;
    private final ServletContext servletContext;

    public MockSpringMvcServlet(Servlet servlet, ServletContext servletContext) {
        this.servlet = servlet;
        this.servletContext = servletContext;
    }

    @Override
    protected Servlet getServlet(HttpRequestBuilder request) {
        return servlet;
    }

    @Override
    protected ServletContext getServletContext() {
        return servletContext;
    }

    private static final ServletContext SERVLET_CONTEXT = new MockServletContext();
    private static final Servlet SERVLET;

    static {
        SERVLET = initServlet();
    }

    private static Servlet initServlet() {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(MockConfig.class);
        context.setServletContext(SERVLET_CONTEXT);
        DispatcherServlet servlet = new DispatcherServlet(context);
        ServletConfig servletConfig = new MockServletConfig();
        try {
            servlet.init(servletConfig);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return servlet;
    }

    public static MockSpringMvcServlet getMock() {
        return new MockSpringMvcServlet(SERVLET, SERVLET_CONTEXT);
    }

}

但是,MockConfig.class在EnableAutoConfiguration批注上失败:

@Configuration
@EnableAutoConfiguration
// @PropertySource("classpath:application.yml") // only for key/value pair .properties files and not YAML
public class MockConfig {

    // Global Exception Handler ...


    // Services ...


    // Controllers ...
    @Bean
    public NumbersAPI numbersAPI() {
        return new NumbersAPI();
    }


}

例外是:

2019-04-23 15:13:21.297  INFO   --- [           main] com.intuit.karate                        : karate.env system property was: null 
2019-04-23 15:13:21.306  INFO   --- [           main] com.intuit.karate                        : karate environment set to: dev 
2019-04-23 15:13:21.429  INFO   --- [           main] com.intuit.karate                        : baseUrl configured for API tests: http://localhost:50000 
2019-04-23 15:13:21.469  INFO   --- [           main] o.s.mock.web.MockServletContext          : Initializing Spring FrameworkServlet ''
2019-04-23 15:13:21.469  INFO   --- [           main] o.s.web.servlet.DispatcherServlet        : FrameworkServlet '': initialization started
2019-04-23 15:13:21.496  INFO   --- [           main] .s.AnnotationConfigWebApplicationContext : Refreshing WebApplicationContext for namespace '-servlet': startup date [Tue Apr 23 15:13:21 EDT 2019]; root of context hierarchy
2019-04-23 15:13:21.535  INFO   --- [           main] .s.AnnotationConfigWebApplicationContext : Registering annotated classes: [class MockConfig]
2019-04-23 15:13:22.215  INFO   --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'dataSource' with a different definition: replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Dbcp2; factoryMethodName=dataSource; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Dbcp2.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Tomcat; factoryMethodName=dataSource; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Tomcat.class]]
2019-04-23 15:13:22.330  WARN   --- [           main] o.s.b.a.AutoConfigurationPackages        : @EnableAutoConfiguration was declared on a class in the default package. Automatic @Repository and @Entity scanning is not enabled.
2019-04-23 15:13:22.714  INFO   --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.amqp.rabbit.annotation.RabbitBootstrapConfiguration' of type [org.springframework.amqp.rabbit.annotation.RabbitBootstrapConfiguration$$EnhancerBySpringCGLIB$$cafe2407] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2019-04-23 15:13:23.212  WARN   --- [           main] .s.AnnotationConfigWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Tomcat.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.tomcat.jdbc.pool.DataSource]: Factory method 'dataSource' threw exception; nested exception is org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Cannot determine embedded database driver class for database type NONE. If you want an embedded database please put a supported one on the classpath. If you have database settings to be loaded from a particular profile you may need to active it (no profiles are currently active).
2019-04-23 15:13:23.215 ERROR   --- [           main] o.s.web.servlet.DispatcherServlet        : Context initialization failed

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Tomcat.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.tomcat.jdbc.pool.DataSource]: Factory method 'dataSource' threw exception; nested exception is org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Cannot determine embedded database driver class for database type NONE. If you want an embedded database please put a supported one on the classpath. If you have database settings to be loaded from a particular profile you may need to active it (no profiles are currently active).

我已经使用空手道模拟servlet github项目的文档作为指导来使其他空手道测试成功工作,但是我不知道如何使用应用程序YAML文件为空手道测试配置spring上下文。

2 个答案:

答案 0 :(得分:1)

@PropertySource仅支持您已经发现的属性文件。

看看以下SOF问题: Spring @PropertySource using YAML

希望这会有所帮助。

答案 1 :(得分:0)

通过进行以下更改,我能够使Spring尊重并加载application.yml文件:

  1. 将YamlPropertySourceFactory util类添加到测试包:

import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

public class YamlPropertySourceFactory implements PropertySourceFactory {

    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        Properties propertiesFromYaml = loadYamlIntoProperties(resource);
        String sourceName = name != null ? name : resource.getResource().getFilename();
        return new PropertiesPropertySource(sourceName, propertiesFromYaml);
    }

    private Properties loadYamlIntoProperties(EncodedResource resource) throws FileNotFoundException {
        try {
            YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
            factory.setResources(resource.getResource());
            factory.afterPropertiesSet();
            return factory.getObject();
        } catch (IllegalStateException e) {
            // for ignoreResourceNotFound
            Throwable cause = e.getCause();
            if (cause instanceof FileNotFoundException)
                throw (FileNotFoundException) e.getCause();
            throw e;
        }
    }
}

  1. 在MockConfig类的@PropertySource批注中引用上述内容,如下所示:

@Configuration
@EnableAutoConfiguration
@PropertySource(factory = YamlPropertySourceFactory.class, value = "classpath:application.yml")
public class MockConfig {

    // Global Exception Handler ...
    @Bean
    public ExampleAPIExceptionHandler exampleAPIExceptionHandler() {
        return new ExampleAPIExceptionHandler();
    }


    // Services ...


    // Controllers ...
    @Bean
    public NumbersAPI numbersAPI() {
        return new NumbersAPI();
    }
}

Spring上下文加载并且所有空手道测试通过。

如果有一种使用空手道将YAML文件作为Spring上下文加载的简单方法,请发布该文件。目前,这将起作用。

参考:Use @PropertySource with YAML files