java 存根
每个具有数据库的项目都有两难选择:如何测试与数据库有关的代码。 有几种选择(不互斥):
- 使用模拟–假设DAO与数据库之间的通信有效,请仅使用单元测试并模拟数据访问层
- 使用每个测试启动和关闭的嵌入式数据库。 这也可以视为单元测试
- 使用部署在某个地方(本地或测试环境)的真实数据库。 困难的部分是确保它始终处于干净状态。
- 在将应用程序部署到具有适当数据库的测试服务器上之后,请使用端到端/功能测试/ bdd / UI测试。
以上都不是没有问题。 具有模拟DAO的单元测试无法真正测试依赖于数据库状态的更复杂的交互。 嵌入式数据库并不总是可用的(例如,如果您使用的是非关系数据库,或者如果您依赖RDBMS特定的功能,则HSQLDB不会这样做),否则它们的启动速度可能会很慢,因此您的测试可能会花费很长时间支持。 实际的数据库安装会使设置复杂化,保持其清洁并非总是那么容易。 端到端测试的覆盖范围很难衡量,并且不一定涵盖所有边缘情况,因为它们比单元测试和集成测试更难维护。
我最近尝试了一种奇怪的方法,该方法到目前为止效果很好–对数据库进行存根 。 它更适用于键值存储,而不适用于关系数据库。
就我而言,即使有嵌入的cassandra ,启动起来也很慢,安装起来并不容易,并且存在一些细微的问题。 这就是为什么我用内存中的ConcurrentHashMap
替换了整个内容。
由于我使用的是spring-data-cassandra,因此我扩展了CassandraTemplate
类,并在新的StubCassandraTemplate
实现了所有方法,并在测试spring上下文中使用了它而不是常规方法。 存根可以很轻松地支持所有键/值操作,并且您可以进行更复杂的集成测试(当然,拥有非常复杂的测试不是一个好主意,但是单元测试可能太简单或太依赖于一个很多模拟)。 以下是代码摘录:
@Component("cassandraTemplate")
public class StubCassandraTemplate extends CassandraTemplate {
private Map<Class<?>, Map<Object, Object>> data = new ConcurrentHashMap<>();
@Override
public void afterPropertiesSet() {
// no validation
}
@SuppressWarnings("unchecked")
@Override
public <T> T insert(T entity) {
List<Field> pk = FieldUtils.getFieldsListWithAnnotation(entity.getClass(), PrimaryKey.class);
initializeClass(entity.getClass());
try {
pk.get(0).setAccessible(true);
return (T) data.get(entity.getClass()).put(pk.get(0).get(entity), entity);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException(e);
}
}
private <T> void initializeClass(Class<?> clazz) {
if (data.get(clazz) == null) {
data.put(clazz, new ConcurrentHashMap<>());
}
}
....
}
Cassandra支持诸如CQL(查询语言)之类的一些高级功能,它不像诸如get和put这样的键值操作那么容易存根,但实际上并不难。 尤其是如果您不依赖复杂的where子句(无论如何在Cassandra中都是不好的做法),都可以使用regex轻松解析查询并在ConcurrentHashMap
找到合适的条目。
键值存储是此方法的理想选择,因为在集成测试方案中不需要其主要优点(易于水平扩展)。 您只需要验证代码是否正确处理了与数据库的交互,包括放置在数据库中的内容和返回的内容。 这种交互的确切实现方式-无论是在内存中还是使用二进制协议,都可能超出范围。
请注意,这些测试不能保证应用程序将与真实数据库一起使用。 他们仅保证如果数据库的行为与内存中的键值数据结构相同,则它将正常运行。 通常这是假设,但并不总是正确的-例如,数据库可以施加存根实现所没有的其他约束。 例如,Cassandra不允许对非索引列进行WHERE查询。 如果您不考虑这一点,很显然,您的测试将通过,但是您的应用程序将被破坏。
这就是为什么您仍然需要端到端测试以及可能需要一些真正的集成测试的原因,但是您可以使用一个简单的内存存根覆盖大多数代码,而仅执行一些“合理的”完全集成测试。
翻译自: https://www.javacodegeeks.com/2017/08/stubbing-key-value-stores.html
java 存根