使用 querydsl 模拟数据库查询 - 可选问题
Mocking db queries with querydsl - issue with Optional
我在为我的应用程序编写数据库查询测试时遇到了一些困难,它在 mongo 之上使用了 querydsl。我发现了几个只对查询对象本身进行单元测试的人的例子,但我想更进一步,测试查询的执行方式(如集成测试),但不必启动整个数据库进程。也就是说,使用 java 工具专门模拟数据库。
我找不到任何相关信息,DBUnit 或 DbSetup 等工具需要与数据库的实际连接。所以我开始写我自己的 classes,而且它几乎可以工作。这个想法是使用 com.mysema.query.collections.CollQuery 和 mockito 来模拟一个数据库,该数据库将接收我的应用程序的查询,并带有包装器 "CollQuery to Query"。基本上,它是这样工作的:
public class MyServiceTest {
private MyService service;
private final Collection<MyObject> fakeTable = new ArrayList<>();
@Before
public void setup() {
final Persister persister = mock(Persister.class);
when(persister.query(any(Class.class)))
// MockedQuery is the wrapper I wrote
.thenReturn(new MockedQuery<>(QMyObject.myObject, fakeTable));
service = new MyService(persister);
}
@Test
public void shouldWork() {
fakeTable.add(new MyObject("one"));
fakeTable.add(new MyObject("two"));
fakeTable.add(new MyObject("three"));
final List<MyObject> result = service.getOne();
// service.getOne would do something like:
// persister.query(QMyObject.myObject).where(QMyObject.myObject.name.eq("one")).list()
assertThat(result).hasSize(1);
}
}
...基本上,它似乎有效!除了我的代码重度使用了guava的Optional,而且好像是querydsl的问题。如果 MyObject 不是字符串,而是采用可选 < 字符串 > ,那么我会收到错误消息:
com.mysema.codegen.CodegenException: Compilation of public class Q_01363784216_1275614662_1275614662_1573163836_01698119955_566403833 {
public static Iterable<xxx.OpenedInterruption> eval(Iterable<xxx.OpenedInterruption> openedInterruption_, xxx.InterruptionType a1, xxx.InterruptionTargetType a2, com.google.common.base.Present a3) {
java.util.List<xxx.OpenedInterruption> rv = new java.util.ArrayList<xxx.OpenedInterruption>();
for (xxx.OpenedInterruption openedInterruption : openedInterruption_) {
try {
if (com.mysema.query.collections.CollQueryFunctions.equals(com.mysema.query.collections.CollQueryFunctions.<xxx.InterruptionType>get(openedInterruption, "type"), a1) && com.mysema.query.collections.CollQueryFunctions.equals(com.mysema.query.collections.CollQueryFunctions.<xxx.InterruptionTargetType>get(openedInterruption, "targetType"), a2) && com.mysema.query.collections.CollQueryFunctions.equals(com.mysema.query.collections.CollQueryFunctions.<com.google.common.base.Optional>get(openedInterruption, "target"), a3)) {
rv.add(openedInterruption);
}
} catch (NullPointerException npe) { }
}
return rv; }
}
failed.
/Q_01363784216_1275614662_1275614662_1573163836_01698119955_566403833.java:3: error: Present is not public in com.google.common.base; cannot be accessed from outside package
public static Iterable<xxx.OpenedInterruption> eval(Iterable<xxx.OpenedInterruption> openedInterruption_, xxx.InterruptionType a1, xxx.InterruptionTargetType a2, com.google.common.base.Present a3) {
^
1 error
at com.mysema.codegen.JDKEvaluatorFactory.compile(JDKEvaluatorFactory.java:74)
at com.mysema.codegen.AbstractEvaluatorFactory.createEvaluator(AbstractEvaluatorFactory.java:128)
at com.mysema.query.collections.DefaultEvaluatorFactory.createEvaluator(DefaultEvaluatorFactory.java:157)
at com.mysema.query.collections.DefaultQueryEngine.evaluateSingleSource(DefaultQueryEngine.java:176)
at com.mysema.query.collections.DefaultQueryEngine.list(DefaultQueryEngine.java:91)
at com.mysema.query.collections.AbstractCollQuery.list(AbstractCollQuery.java:219)
at xxx.BusinessInterruptionServiceImplTest$MockedQuery.list(BusinessInterruptionServiceImplTest.java:143)
at xxx.BusinessInterruptionServiceImplTest.setup(BusinessInterruptionServiceImplTest.java:79)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access[=11=]0(ParentRunner.java:53)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
所以,看起来 mysema 试图实例化 com.google.common.base.Present ,但无法这样做。奇怪的是,同一个查询与我的生产代码完美配合,所以问题可能与我用于模拟的 "CollQuery" class 非常具体。
知道如何解决这个问题吗?
或者,如果有人能像我正在尝试的那样看到模拟数据库的不同方法,我将不胜感激!
谢谢
PS:这是我的包装器 "MockedQuery" class(非常简单):
public class MockedQuery<T> implements Query<T> {
private final Path<T> path;
private final CollQuery collQuery;
public MockedQuery(final Path<T> path, final Iterable<T> collection) {
this.path = path;
collQuery = from(path, collection);
}
@Override
public boolean exists() {
return collQuery.exists();
}
@Override
public boolean notExists() {
return !exists();
}
@Override
public CloseableIterator<T> iterate() {
return collQuery.iterate(path);
}
@Override
public List<T> list() {
return collQuery.list(path);
}
@Nullable
@Override
public T singleResult() {
return collQuery.singleResult(path);
}
@Nullable
@Override
public T uniqueResult() {
return collQuery.uniqueResult(path);
}
@Override
public SearchResults<T> listResults() {
return collQuery.listResults(path);
}
@Override
public long count() {
return collQuery.count();
}
@Override
public Query<T> limit(final long limit) {
collQuery.limit(limit);
return this;
}
@Override
public Query<T> offset(final long offset) {
collQuery.offset(offset);
return this;
}
@Override
public Query<T> restrict(final QueryModifiers modifiers) {
collQuery.restrict(modifiers);
return this;
}
@Override
public Query<T> orderBy(final OrderSpecifier<?>... o) {
collQuery.orderBy(o);
return this;
}
@Override
public <U> Query<T> set(final ParamExpression<U> param, final U value) {
collQuery.set(param, value);
return this;
}
@Override
public Query<T> distinct() {
collQuery.distinct();
return this;
}
@Override
public Query<T> where(final Predicate... o) {
collQuery.where(o);
return this;
}
}
有关信息,已在 querydsl 3.6.3 中修复
我在为我的应用程序编写数据库查询测试时遇到了一些困难,它在 mongo 之上使用了 querydsl。我发现了几个只对查询对象本身进行单元测试的人的例子,但我想更进一步,测试查询的执行方式(如集成测试),但不必启动整个数据库进程。也就是说,使用 java 工具专门模拟数据库。
我找不到任何相关信息,DBUnit 或 DbSetup 等工具需要与数据库的实际连接。所以我开始写我自己的 classes,而且它几乎可以工作。这个想法是使用 com.mysema.query.collections.CollQuery 和 mockito 来模拟一个数据库,该数据库将接收我的应用程序的查询,并带有包装器 "CollQuery to Query"。基本上,它是这样工作的:
public class MyServiceTest {
private MyService service;
private final Collection<MyObject> fakeTable = new ArrayList<>();
@Before
public void setup() {
final Persister persister = mock(Persister.class);
when(persister.query(any(Class.class)))
// MockedQuery is the wrapper I wrote
.thenReturn(new MockedQuery<>(QMyObject.myObject, fakeTable));
service = new MyService(persister);
}
@Test
public void shouldWork() {
fakeTable.add(new MyObject("one"));
fakeTable.add(new MyObject("two"));
fakeTable.add(new MyObject("three"));
final List<MyObject> result = service.getOne();
// service.getOne would do something like:
// persister.query(QMyObject.myObject).where(QMyObject.myObject.name.eq("one")).list()
assertThat(result).hasSize(1);
}
}
...基本上,它似乎有效!除了我的代码重度使用了guava的Optional,而且好像是querydsl的问题。如果 MyObject 不是字符串,而是采用可选 < 字符串 > ,那么我会收到错误消息:
com.mysema.codegen.CodegenException: Compilation of public class Q_01363784216_1275614662_1275614662_1573163836_01698119955_566403833 {
public static Iterable<xxx.OpenedInterruption> eval(Iterable<xxx.OpenedInterruption> openedInterruption_, xxx.InterruptionType a1, xxx.InterruptionTargetType a2, com.google.common.base.Present a3) {
java.util.List<xxx.OpenedInterruption> rv = new java.util.ArrayList<xxx.OpenedInterruption>();
for (xxx.OpenedInterruption openedInterruption : openedInterruption_) {
try {
if (com.mysema.query.collections.CollQueryFunctions.equals(com.mysema.query.collections.CollQueryFunctions.<xxx.InterruptionType>get(openedInterruption, "type"), a1) && com.mysema.query.collections.CollQueryFunctions.equals(com.mysema.query.collections.CollQueryFunctions.<xxx.InterruptionTargetType>get(openedInterruption, "targetType"), a2) && com.mysema.query.collections.CollQueryFunctions.equals(com.mysema.query.collections.CollQueryFunctions.<com.google.common.base.Optional>get(openedInterruption, "target"), a3)) {
rv.add(openedInterruption);
}
} catch (NullPointerException npe) { }
}
return rv; }
}
failed.
/Q_01363784216_1275614662_1275614662_1573163836_01698119955_566403833.java:3: error: Present is not public in com.google.common.base; cannot be accessed from outside package
public static Iterable<xxx.OpenedInterruption> eval(Iterable<xxx.OpenedInterruption> openedInterruption_, xxx.InterruptionType a1, xxx.InterruptionTargetType a2, com.google.common.base.Present a3) {
^
1 error
at com.mysema.codegen.JDKEvaluatorFactory.compile(JDKEvaluatorFactory.java:74)
at com.mysema.codegen.AbstractEvaluatorFactory.createEvaluator(AbstractEvaluatorFactory.java:128)
at com.mysema.query.collections.DefaultEvaluatorFactory.createEvaluator(DefaultEvaluatorFactory.java:157)
at com.mysema.query.collections.DefaultQueryEngine.evaluateSingleSource(DefaultQueryEngine.java:176)
at com.mysema.query.collections.DefaultQueryEngine.list(DefaultQueryEngine.java:91)
at com.mysema.query.collections.AbstractCollQuery.list(AbstractCollQuery.java:219)
at xxx.BusinessInterruptionServiceImplTest$MockedQuery.list(BusinessInterruptionServiceImplTest.java:143)
at xxx.BusinessInterruptionServiceImplTest.setup(BusinessInterruptionServiceImplTest.java:79)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access[=11=]0(ParentRunner.java:53)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
所以,看起来 mysema 试图实例化 com.google.common.base.Present ,但无法这样做。奇怪的是,同一个查询与我的生产代码完美配合,所以问题可能与我用于模拟的 "CollQuery" class 非常具体。
知道如何解决这个问题吗?
或者,如果有人能像我正在尝试的那样看到模拟数据库的不同方法,我将不胜感激!
谢谢
PS:这是我的包装器 "MockedQuery" class(非常简单):
public class MockedQuery<T> implements Query<T> {
private final Path<T> path;
private final CollQuery collQuery;
public MockedQuery(final Path<T> path, final Iterable<T> collection) {
this.path = path;
collQuery = from(path, collection);
}
@Override
public boolean exists() {
return collQuery.exists();
}
@Override
public boolean notExists() {
return !exists();
}
@Override
public CloseableIterator<T> iterate() {
return collQuery.iterate(path);
}
@Override
public List<T> list() {
return collQuery.list(path);
}
@Nullable
@Override
public T singleResult() {
return collQuery.singleResult(path);
}
@Nullable
@Override
public T uniqueResult() {
return collQuery.uniqueResult(path);
}
@Override
public SearchResults<T> listResults() {
return collQuery.listResults(path);
}
@Override
public long count() {
return collQuery.count();
}
@Override
public Query<T> limit(final long limit) {
collQuery.limit(limit);
return this;
}
@Override
public Query<T> offset(final long offset) {
collQuery.offset(offset);
return this;
}
@Override
public Query<T> restrict(final QueryModifiers modifiers) {
collQuery.restrict(modifiers);
return this;
}
@Override
public Query<T> orderBy(final OrderSpecifier<?>... o) {
collQuery.orderBy(o);
return this;
}
@Override
public <U> Query<T> set(final ParamExpression<U> param, final U value) {
collQuery.set(param, value);
return this;
}
@Override
public Query<T> distinct() {
collQuery.distinct();
return this;
}
@Override
public Query<T> where(final Predicate... o) {
collQuery.where(o);
return this;
}
}
有关信息,已在 querydsl 3.6.3 中修复