【问题标题】:How can I start an embedded Cassandra server *before* loading the Spring Context in a Spock test?如何在 Spock 测试中加载 Spring 上下文之前*启动嵌入式 Cassandra 服务器?
【发布时间】:2016-02-23 17:40:03
【问题描述】:

在我的一个集成测试中,我似乎遇到了一个棘手的情况。我有一个基于 Spring Boot/Spring MVC 和 Cassandra DB 的 REST 服务器。我正在使用 spring-data-cassandra jar 文件使 POJO 能够通过 CassandraTemplate 通过 CrudRepository 实现插入到数据库中。该应用程序运行良好,我可以进行 REST 调用,并且框架可以正确地将我的表单数据转换为 POJO 并将数据插入数据库中。到目前为止,一切都很好。

我要做的是编写一个执行整个堆栈的集成测试。显然我不想依赖可用的外部数据库,所以我使用 cassandra-unit-spring 中的 EmbeddedCassandra 东西并通过 Spock 测试驱动它。我遇到的问题似乎与 @ContextConfiguration 注释中定义的 SpringApplicationContextLoader 和 @TestExecutionListener 注释中定义的 CassandraUnitTestExecutionListener 类之间的某种排序冲突有关。由于这是一个 Spock 测试,它需要 SpringApplicationContextLoader 类来自动启动 Spring Boot 服务器。但是,它会在 Cassandra 服务器启动之前执行此操作,这会导致集群/会话 bean 无法加载,因为它们显然会在创建后立即联系服务器。我已经尝试了很多让我头晕目眩的组合。我不得不求助于我认为非常丑陋的解决方案。我有一个 setup() 方法,它使用布尔值来确保上下文和 Spring Boot 应用程序只运行一次。我试着把它放在一个 setupSpec() 方法中,每个测试类只运行一次,但这没有用(有些 catch 22 问题)。如果我尝试 @Autowire 上下文,那么它会在 CassandraServer 启动之前被注入。正如我所提到的,我尝试了无数不同的事情。

此解决方案有效,但让我感到困扰的是,我无法仅使用注释和 DI 以更优雅的方式使其工作。此外,@Value 注释字段没有被初始化,这就是为什么我不得不求助于在启动 Tomcat 后通过环境获取服务器端口的原因。非常欢迎任何有关如何以“正确方式”执行此操作的建议。谢谢。

@ActiveProfiles(profiles = ["test"])
@ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = Application.class)
// @WebIntegrationTest("server.port:0") // Pick a random port for Tomcat
@EnableConfigurationProperties
@EmbeddedCassandra
@CassandraDataSet(value = ["cql/createUserMgmtKeySpace.cql", "cql/createUserMgmtTables.cql"], keyspace = "user_mgmt")
@TestExecutionListeners(listeners = [ CassandraUnitTestExecutionListener.class ]) //, DependencyInjectionTestExecutionListener.class ]) //, mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
@Title("Integration test for the lineup ingestion REST server and the Cassandra DAOs.")
public class CassandraSpec extends Specification {
  @Shared
  ApplicationContext context
  @Shared
  CQLDataLoader cql
  @Shared boolean initialized = false

  // This is the server port that Spring picked. We use it in the REST URLs
  //  @Value('${local.server.port}')
  @Shared
  int serverPort

  def setup() {
    if (initialized) {
      return
    }

    System.setProperty("spring.profiles.active", "test")
    SpringApplication app = new SpringApplication(CassandraConfig.class)
    app.headless = true
    context = app.run(Application.class)
    assert context
    cql = new CQLDataLoader(context.getBean("session"))
    serverPort = Integer.parseInt(context.environment.getProperty('server.port'))
    println("Tomcat port: ${serverPort}")
    initialized = true
  }

... test methods here ...
}

【问题讨论】:

    标签: spring cassandra spring-boot spring-data spock


    【解决方案1】:

    掌心!!!发布这个之后,我发现我需要添加 CassandraUnitDependencyInjectionTestExecutionListener。神奇的线是:

    @TestExecutionListeners(listeners = [ CassandraUnitDependencyInjectionTestExecutionListener, CassandraUnitTestExecutionListener, DependencyInjectionTestExecutionListener.class ]) 
    

    之后,我能够在没有任何其他问题的情况下运行测试,并且可以注入一个临时端口并在我的 RestTemplate URL 中使用它。问题解决了:-)

    【讨论】:

      【解决方案2】:

      @user2337270 提供的上述答案是正确的。虽然我没有使用 Spock 测试框架,但我花了相当多的时间尝试让 cassandra 单元和 spring 工作。添加CassandraUnitDependencyInjectionTestExecutionListener 就可以了。我认为提供一个有效的完整测试类的样本可能对其他用户有帮助。希望对您有所帮助。

      @RunWith(SpringJUnit4ClassRunner.class)
      @ContextConfiguration(classes = { CassandraConfig.class, InboundDocumentCassandraDaoTest.CassandraPropertiesTest.class })
      @EmbeddedCassandra
      @CassandraDataSet(value = {"idoc.cql"}, keyspace = "idoc")
      @TestExecutionListeners(listeners = {     CassandraUnitDependencyInjectionTestExecutionListener.class,  DependencyInjectionTestExecutionListener.class  })
      public class InboundDocumentCassandraDaoTest {
      
          @Autowired
          CassandraOperations cassOp;
      
          @Autowired
          InboundDocumentDao dao;
      
          @Test
          public void insert_into_idocs() throws Exception {
      
              UUID id = UUIDs.timeBased();
              long addedDate = System.currentTimeMillis();
      
              Insert insert = QueryBuilder.insertInto("idocs");
              insert.setConsistencyLevel(ConsistencyLevel.ONE);
              insert.value("idoc_id", id);
              insert.value("idoc_added_date", addedDate);
              insert.value("idoc_type", "invoice");
              insert.value("json_doc", "{ \"foo\":\"bar\" }");
      
              cassOp.execute(insert);
      
              String jsonObj = dao.getInboundDocumentById(id.toString());
              assertNotNull(jsonObj);
              assertEquals("{ \"foo\":\"bar\" }", jsonObj);
          }
      
          @Configuration
          public static class CassandraPropertiesTest {
              @Bean
              public CassandraProperties cassadraProps() {
                  CassandraProperties props = new CassandraProperties();
                  props.setCassandraContactpoints("127.0.0.1");
                  props.setCassandraPort("9142");
                  props.setCassandraKeyspace("idoc");
                  props.setReadConsistencyLevel("LOCAL_ONE");
                  return props;
              }
          }
      }
      

      作为仅供参考,idoc.cql 文件放置在 src/main/resources 文件夹中。

      【讨论】:

        猜你喜欢
        • 2019-02-18
        • 1970-01-01
        • 2020-08-22
        • 2020-03-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-14
        • 2011-09-30
        相关资源
        最近更新 更多