英文:
How to have Spring @DataJpaTest annotation on subclass instead of having it be required on the abstract superclass
问题
I want to create an abstract test class for my (Spring) repositories, to be implemented twice: once using the actual JPA repository, once using an in-memory fake.
For this I have the following setup:
An abstract superclass, which requires the @DataJpaTest
annotation
@DataJpaTest
public abstract class RepositoryTest {
abstract TestRepo repo();
abstract void save(Entity entity);
@Test
void testtest() {
save(new Entity("ProviderId", new Date(), "event", "phase", "cat", "batch", "me" +
"message"));
assertThat(repo().findAll()).isNotNull();
}
}
and a concrete subclass, which requires some other Spring annotations:
@ContextConfiguration(classes = TestRepo.class)
@EnableAutoConfiguration
public class JpaRepositoryTest extends RepositoryTest {
@Autowired
TestEntityManager em;
@Override
void save(Entity entity) {
em.persist(entity);
}
@Override
TestRepo repo() {
return jpaTestRepo;
}
}
(The in-memory test is trivial, the save method just saves to the fake repo's in-memory map directly)
Because I'm structuring my code as a ports & adapters architecture, and using Gradle modules to enforce the boundaries between the ports & adapters, the RepositoryTest and the JpaRepositoryTest are in different modules. The former is in the domain module, which should not depend on any framework such as Spring.
I am, however, forced to add the @DataJpaTest on the abstract superclass (in the domain module) or otherwise, I get the (confusing error):
java.lang.IllegalStateException: No transactional EntityManager found, is your test running in a transaction?
Is there any way to set up the JpaRepositoryTest to have all the Spring annotations and keep the RepositoryTest itself free from Spring?
英文:
I want to create an abstract test class for my (Spring) repositories, to be implemented twice: once using the actual JPA repository, once using an in memory fake. (See https://wiki.c2.com/?AbstractTestCases)
For this I have the following setup:
An abstract superclass, which requires the @DataJpaTest
annotation
@DataJpaTest
public abstract class RepositoryTest {
abstract TestRepo repo();
abstract void save(Entity entity);
@Test
void testtest() {
save(new Entity("ProviderId", new Date(), "event", "phase", "cat", "batch", "me" +
"message"));
assertThat(repo().findAll()).isNotNull();
}
}
and a concrete subclass, which requires some other Spring annotations:
@ContextConfiguration(classes = TestRepo.class)
@EnableAutoConfiguration
public class JpaRepositoryTest extends RepositoryTest {
@Autowired
TestEntityManager em;
@Override
void save(Entity entity) {
em.persist(entity);
}
@Override
TestRepo repo() {
return jpaTestRepo;
}
}
(The in-memory test is trivial, the save method just saves to the fake repo's in-memory map directly)
Because I'm structuring my code as a ports & adapters architecture, and using Gradle modules to enforce the boundaries between the ports & adapters, the RepositoryTest
and the JpaRepositoryTest
are in different modules. The former is in the domain module, which should not depend on any framework such as Spring.
I am however forced to add the @DataJpaTest
on the abstract superclass (in the domain module) or otherwise I get the (confusing error):
> java.lang.IllegalStateException: No transactional EntityManager found, is your test running in a transaction?
Is there any way to setup the JpaRepositoryTest
to have all the Spring annotations, and keep the `RepositoryTest itself free from Spring?
答案1
得分: 1
我被迫在抽象超类(在领域模块中)上添加@DataJpaTest注解,否则会出现(令人困惑的错误)。
出现异常的原因是您不能在没有@AutoConfigureTestEntityManager注解的情况下使用TestEntityManager,而@AutoConfigureTestEntityManager又是@DataJpaTest的一部分(请参阅问题28067的修复):
如果您打算同时使用内存和真实的JPA存储库,您可以使用带有@DataJpaTest的适配器和纯抽象类:
public abstract class RepositoryTest {
abstract TestRepo repo();
abstract void save(Entity entity);
@Test
void testtest() {
save(new Entity("ProviderId", new Date(), "event", "phase", "cat", "batch", "message"));
assertThat(repo().findAll()).isNotNull();
}
}
@DataJpaTest
public abstract class InMemoryRepositoryTestAdapter extends RepositoryTest {
}
然后,专用于虚假存储库的测试类是从InMemoryRepositoryTestAdapter子类继承的,而与“真实”JPA有关的测试类则是从RepositoryTest继承的。
请注意,您不能在“真实”的JPA存储库中使用TestEntityManager,我建议在测试设置中使用存储库本身。
英文:
> I am however forced to add the @DataJpaTest on the abstract superclass (in the domain module) or otherwise I get the (confusing error)
You face the exception because you cannot use TestEntityManager
without @AutoConfigureTestEntityManager
, which is in turn a member of @DataJpaTest
(see the fix for the issue 28067):
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(DataJpaTestContextBootstrapper.class)
@ExtendWith({SpringExtension.class})
@OverrideAutoConfiguration(
enabled = false
)
@TypeExcludeFilters({DataJpaTypeExcludeFilter.class})
@Transactional
@AutoConfigureCache
@AutoConfigureDataJpa
@AutoConfigureTestDatabase
@AutoConfigureTestEntityManager // <----
@ImportAutoConfiguration
public @interface DataJpaTest {}
If your intention is to use both in-memory and real JPA repos you could use adapter bearing @DataJpaTest
and plain abstract class:
public abstract class RepositoryTest {
abstract TestRepo repo();
abstract void save(Entity entity);
@Test
void testtest() {
save(new Entity("ProviderId", new Date(), "event", "phase", "cat", "batch", "me" +
"message"));
assertThat(repo().findAll()).isNotNull();
}
}
@DataJpaTest
public abstract class InMemoryRepositoryTestAdapter extends RepositoryTest {
}
The test classes dedicated for fake repos are then sub-classed from InMemoryRepositoryTestAdapter
and the ones dealing with "real" JPA are inherited from RepositoryTest
.
Pay attention, that you cannot use TestEntityManager
with "real" JPA repositories and I'd use the repos themselves for test set-up.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论