如何在Spock测试中加载Spring Context之前如何启动嵌入式Cassandra服务器*?

时间:2015-11-21 05:48:20

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

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

我要做的是编写一个执行整个堆栈的集成测试。显然我不想依赖外部数据库,所以我使用 cassandra-unit-spring 中的EmbeddedCassandra并通过Spock测试驱动它。我遇到的问题似乎与@ContextConfiguration批注中定义的SpringApplicationContextLoader和@TestExecutionListener批注中定义的CassandraUnitTestExecutionListener类之间的某种排序冲突有关。由于这是一个Spock测试,它需要SpringApplicationContextLoader类来自动启动Spring Boot服务器。但是,它在Cassandra服务器启动之前会这样做,这会导致集群/会话bean无法加载,因为它们显然会在创建后立即尝试联系服务器。我已经尝试了很多东西组合,我的头在旋转。我不得不求助于我认为非常难看的解决方案。我有一个setup()方法,它使用布尔值来确保上下文和Spring Boot应用程序只运行一次。我尝试将它放在一个setupSpec()方法中,该方法每个测试类只运行一次,但是没有用(有些捕获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 ...
}

2 个答案:

答案 0 :(得分:5)

脸掌!!!发布后,我发现我需要添加CassandraUnitDependencyInjectionTestExecutionListener。神奇的界限是:

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

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

答案 1 :(得分:1)

@ 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;
        }
    }
}

作为FYI,idoc.cql文件放在src/main/resources文件夹中。