在我的一次集成测试中,我似乎遇到了一个棘手的情况。我有一个基于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 ...
}
答案 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
文件夹中。