如何测试 Spring Data 存储库?

2023-12-10

我想要一个存储库(比如说,UserRepository)在 Spring Data 的帮助下创建。我是 spring-data 的新手(但不是 spring),我使用这个tutorial。我选择的处理数据库的技术是 JPA 2.1 和 Hibernate。问题是我对如何为这样的存储库编写单元测试一无所知。

让我们来create()方法为例。由于我正在以测试为先,因此我应该为其编写一个单元测试 - 这就是我遇到三个问题的地方:

  • 首先,我如何注入一个模拟EntityManager进入不存在的实现UserRepository界面? Spring Data 将生成基于此接口的实现:

    public interface UserRepository extends CrudRepository<User, Long> {}
    

    但是,我不知道如何强制它使用EntityManager模拟和其他模拟 - 如果我自己编写实现,我可能会有一个 setter 方法EntityManager,允许我使用我的模拟进行单元测试。 (至于实际的数据库连接,我有一个JpaConfiguration类,注释为@Configuration and @EnableJpaRepositories,它以编程方式定义 beanDataSource, EntityManagerFactory, EntityManager等等 - 但存储库应该是测试友好的,并允许覆盖这些东西)。

  • 其次,我应该测试交互吗?我很难弄清楚有哪些方法EntityManager and Query应该被称为(类似于verify(entityManager).createNamedQuery(anyString()).getResultList();),因为不是我在编写实现。

  • 第三,我是否应该首先对 Spring-Data 生成的方法进行单元测试?据我所知,第三方库代码不应该进行单元测试 - 只有开发人员自己编写的代码才应该进行单元测试。但如果这是真的,它仍然把第一个问题带回到现场:比如说,我的存储库有几个自定义方法,我将为其编写实现,我如何注入我的模拟EntityManager and Query进入最终生成的存储库?

注意:我将使用以下方式测试我的存储库both集成和单元测试。对于我的集成测试,我使用 HSQL 内存数据库,并且显然我没有使用数据库进行单元测试。

可能是第四个问题,在集成测试中测试正确的对象图创建和对象图检索是否正确(比如说,我有一个用 Hibernate 定义的复杂对象图)?

更新:今天我继续尝试模拟注入 - 我创建了一个静态内部类以允许模拟注入。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@Transactional
@TransactionConfiguration(defaultRollback = true)
public class UserRepositoryTest {

@Configuration
@EnableJpaRepositories(basePackages = "com.anything.repository")
static class TestConfiguration {

    @Bean
    public EntityManagerFactory entityManagerFactory() {
        return mock(EntityManagerFactory.class);
    }

    @Bean
    public EntityManager entityManager() {
        EntityManager entityManagerMock = mock(EntityManager.class);
        //when(entityManagerMock.getMetamodel()).thenReturn(mock(Metamodel.class));
        when(entityManagerMock.getMetamodel()).thenReturn(mock(MetamodelImpl.class));
        return entityManagerMock;
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        return mock(JpaTransactionManager.class);
    }

}

@Autowired
private UserRepository userRepository;

@Autowired
private EntityManager entityManager;

@Test
public void shouldSaveUser() {
    User user = new UserBuilder().build();
    userRepository.save(user);
    verify(entityManager.createNamedQuery(anyString()).executeUpdate());
}

}

但是,运行此测试会给出以下堆栈跟踪:

java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.DefaultTestContext.getApplicationContext(DefaultTestContext.java:101)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:319)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:212)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:232)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:175)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:77)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:195)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:63)
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:120)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepository': Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'entityManager' threw exception; nested exception is java.lang.IllegalArgumentException: JPA Metamodel must not be null!
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1493)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1197)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:684)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:121)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)
    at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:100)
    at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:250)
    at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContextInternal(CacheAwareContextLoaderDelegate.java:64)
    at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:91)
    ... 28 more
Caused by: org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'entityManager' threw exception; nested exception is java.lang.IllegalArgumentException: JPA Metamodel must not be null!
    at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:108)
    at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:62)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1489)
    ... 44 more

tl;dr

简而言之,没有办法对 Spring Data JPA 存储库进行合理的单元测试,原因很简单:模拟我们调用来引导存储库的 JPA API 的所有部分是很麻烦的。无论如何,单元测试在这里没有太大意义,因为您通常不会自己编写任何实现代码(请参阅下面有关自定义实现的段落),因此集成测试是最合理的方法。

Details

我们进行了大量的前期验证和设置,以确保您只能引导没有无效派生查询等的应用程序。

  • 我们创建并缓存CriteriaQuery派生查询的实例,以确保查询方法不包含任何拼写错误。这需要使用 Criteria API 以及 meta.model。
  • 我们通过询问来验证手动定义的查询EntityManager创建一个Query这些实例(有效地触发查询语法验证)。
  • 我们检查Metamodel用于准备 is-new 检查等的有关域类型的元数据。

您可能会推迟在手写存储库中的所有内容,这可能会导致应用程序在运行时中断(由于无效查询等)。

如果您考虑一下,您无需为存储库编写任何代码,因此无需编写任何代码unit测试。根本没有必要,因为您可以依靠我们的测试库来捕获基本错误(如果您仍然碰巧遇到错误,请随时提出ticket)。但是,肯定需要集成测试来测试持久层的两个方面,因为它们是与您的域相关的方面:

  • 实体映射
  • 查询语义(无论如何,每次引导尝试都会验证语法)。

集成测试

这通常是通过使用内存数据库和引导 Spring 的测试用例来完成的ApplicationContext通常通过测试上下文框架(正如您已经做的那样),预填充数据库(通过通过插入对象实例EntityManager或 repo,或通过纯 SQL 文件),然后执行查询方法来验证它们的结果。

测试自定义实现

存储库的自定义实现部分是以某种方式写的他们不必了解 Spring Data JPA。它们是普通的春豆,EntityManager注射。您当然可能想尝试模拟与它的交互,但说实话,对 JPA 进行单元测试对我们来说并不是一次太愉快的经历,而且它可以处理很多间接操作(EntityManager -> CriteriaBuilder, CriteriaQuery等),这样你最终会得到模拟返回模拟等等。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何测试 Spring Data 存储库? 的相关文章

  • 需要帮助理解这段代码

    我正在尝试学习单元测试 我正在尝试对我在 asp net mvc 1 0 中制作的一些会员资格内容进行单元测试 我一直在关注一本关于 MVC 的书 我对一些东西感到困惑 希望有人能为我解答 我的框架使用 Nunit 和 Moq 问题一 pu
  • 如何通过 mat-dialog-close 或其他方式对 MatDialog 是否关闭进行单元测试

    我有一个简单的组件 它将显示为对话框窗口垫对话框 https material angular io components dialog overview 在该组件的模板中 一个按钮标记为垫子对话框关闭 https material ang
  • Android:UiTesting 时运行时错误

    我正在尝试运行 Ui 测试 每次运行应用程序时 它都会在控制台中出现以下错误并关闭正在运行的应用程序 我导入了 uiautomator jar android jar 和 JUnit4 库 我正在使用 Eclipse 我在这里缺少什么 20
  • 在SPRING BOOT中配置多个数据库

    我正在尝试为我的 Spring Boot 应用程序连接 2 个不同的数据库 但出现此错误 应用程序无法启动 描述 com SyncFibertToolSpring SyncFibertTool MydbDB Config MydbDbCon
  • Autofixture 和 WebApi 控制器

    我正在使用 AutoFixture 尝试测试 WebApi 站点的控制器 我正在将 AutoData 功能与 Moq 一起使用 如上所述普洛的博客 http blog ploeh dk 2010 10 08 AutoDataTheories
  • 这可以用 Moq 来嘲笑吗?

    我正在努力模拟一些外部依赖项 并且在一个第三方类中遇到了麻烦 该类在其构造函数中接收另一个第三方类的实例 希望 SO 社区能给我一些指导 我想创建一个模拟实例SomeRelatedLibraryClass它的构造函数接受一个模拟实例Some
  • Spring Data Jpa OneToMany 同时保存子实体和父实体?

    这是我的父实体 注意 为了简洁起见 删除了 getter setter lombok 注释 Entity public class Board Id GeneratedValue strategy GenerationType IDENTI
  • 使用unittest时如何知道每次测试花费的时间?

    Unittest 仅显示运行所有测试所花费的总时间 但不单独显示每个测试所花费的时间 使用unittest时如何添加每个测试的计时 我想 目前不可能 http bugs python org issue4080 http bugs pyth
  • 未注入带有 JPA2 的 Apache Ignite 2.7 IgniteRepository

    使用在 Web 上建立的 guildes 我使用 Spring Data JPA 2 应用程序制作了简单的 Spring Boot 2 仅在 2 7 版本中才向 Apache Ignite 添加了 Spring Boot JPA 2 支持
  • 无需编译的 ES6 单元测试

    我无法找到任何 Mocha 或任何其他通过 Gulp 直接在 ES6 代码上运行的单元测试框架的示例 没有 Babel Webpack 等 我找到了一个在浏览器中使用 ES6 代码运行 Mocha 的示例 经过一些修改 但它不是自动化的 有
  • 如何跨多个文件跨越 javascript 命名空间?

    我永远忽略了javascript 几年前我开始使用 jQuery 这样我就可以过得去 但随着我开始更多地进行 TDD 我昨天决定真正深入研究 javascript 之后可能还有咖啡脚本 在我的 ASP NET Web 窗体应用程序中 我有很
  • 如何使用 Spring Data 和 QueryDSL 执行带分页的 JPAQuery

    我有这个请求 可以很好地处理queryDSL Iterable
  • Spring Data JPA findOne() 更改为Optional 如何使用这个?

    我在学SpringBoot2 0 with Java8 我遵循了一些博客制作教程示例 教程源码为 GetMapping id edit public String edit PathVariable Long id Model model
  • 使用 QTestLib 时抑制 qDebug

    我正在向 Qt 中的项目添加单元测试 并希望使用 QTestLib 我已经设置了测试并且它们运行良好 问题是在项目中我们重写了 qDebug 以输出到我们自己的日志文件 这在运行应用程序时效果很好 问题是当我测试类时 它有时会开始记录 然后
  • 模拟 DBSet,EF 模型优先

    正如标题所说 我遵循模型优先方法 所以我的模型类是自动生成的 如果我想嘲笑DBContext衍生的MyModelContainer其中包含DBSets实体类 阅读一些内容 为了进行单元测试 您需要将其更改为IDBSet 是否可以做到这一点
  • Spring boot:单元测试和配置文件

    我正在对休息控制器进行单元测试 这只是更大应用程序的一小部分 我的应用程序无法识别我的测试上下文 并且出现以下异常 java lang IllegalStateException 无法加载ApplicationContext 这是我的测试课
  • JPA Criteria API group_concat 用法

    我目前正在编写一份报告 其中一个字段需要 group concat CriteriaQuery
  • 模拟类:Mock() 还是 patch()?

    我在用mock http www voidspace org uk python mock index html使用Python 想知道这两种方法中哪一种更好 阅读 更Pythonic 方法一 只需创建一个模拟对象并使用它 代码如下 def
  • 如何避免从模拟对象列表返回模拟

    我正在尝试模拟 责任驱动的设计 在对象需要服务来检索其他对象的情况下 我似乎无法避免从模拟返回模拟 一个例子是检查上个月的账单是否已支付的对象 它需要一个检索账单列表的服务 所以我需要在测试中模拟 billRetrievalService
  • Enzyme - 测试嵌套组件是否正确呈现

    我正在尝试测试当通过简单的布尔值更新状态时 在父组件中其子组件是否正确呈现 在父组件下面 class Parent extends Component render const isReady this state const props

随机推荐

  • 表达式中的变量赋值如何工作?

    这是我以前见过的做法 但并不常见 在对值本身进行求值的同时将变量分配给一个值 或者是对表达式本身进行求值 例子 Outputs The value is 1 value 1 if var value echo The value is va
  • Ext.JSON.decode():您正在尝试解码无效的 JSON 字符串

    我对此很陌生 两周以来一直在尝试解决我的问题 现在希望您能提供帮助 我的 JSON 输出似乎无效 但我不确定我的问题是来自 PHP 还是 extjs 脚本 我有一个组合框 当我单击它时 它应该显示一个选项列表 该列表基本上来自 Sql 表
  • 如何在 Scala 中将 CSV 列读入向量

    我有一个 CSV 文件 我的 CSV 文件 我想创建一个这样的地图 A gt Vector 10 75 10 75 10 47 B gt Vector 164 56 164 99 160 98 C gt Vector 7 1 7 4 9 4
  • 替换列表列表中的元素 PROLOG

    我开发了一个谓词来替换索引的值Index一个列表的List with Value并创建一个新的更新列表NewList replace List Index Value NewList replace T 0 X X T replace H
  • 刷新使用值转换器的绑定

    我有一个绑定到一个对象的 WPF UI 我正在使用 ValueConverter 通过业务规则将属性转换为特定图像 public class ProposalStateImageConverter IValueConverter publi
  • Lucene爬虫(需要建立lucene索引)

    我正在寻找用 java 如果可能 或任何其他语言编写的 Apache Lucene 网络爬虫 爬虫必须使用lucene并创建有效的lucene索引和文档文件 所以这就是nutch被淘汰的原因 有谁知道这样的网络爬虫是否存在并且可以如果答案是
  • 如何将数据从 pandas 数据帧加载到 Spark 数据帧

    我已经使用如下方式通过 pyodbc 连接读取了块数据 import pandas as pd import pyodbc conn pyodbc connect Some connection Details sql SELECT fro
  • 创建实现接口的匿名类

    我想知道是否有一些内联短方法来创建实现接口的类 就像有匿名方法但有实现接口一样 问题是 interface iSomeInterface void DoIt public void myMethod iSomeInterface param
  • 复制自定义对象

    我有一个名为 Layer 的对象 它有一些属性和一些方法 我需要将图层传递给第二个视图控制器 SecondVC view self storyboard instantiateViewControllerWithIdentifier 2VC
  • 如何安全地回显 FOR 变量 %%~p 后跟字符串文字

    我有一个变量 p创建自for f命令 当我尝试将它与一些其他参考文献一起使用时 例如 dp然后写入一些文本 然后它访问不同的变量 set var dpabc txt 代码输出 dpa instead of dp 因此 您必须将 FOR F
  • 什么时候应该使用 HashSet 类型?

    我正在探索HashSet
  • 如何从多个简单数组创建结构化数组

    import numpy as np a np array 1 2 3 4 5 6 7 8 9 b np array a b c d e f g h i c np array 9 8 7 6 5 4 3 2 1 datatype np dt
  • League\\Flysystem\\AwsS3v3\\AwsS3Adapter::__construct():参数 #1 ($client) 的类型必须是 Aws\\S3Client,给定的 Aws\\S3\\S3Client

    我已经通过在我的目录中运行以下 Composer 命令安装了 s3 Flysystem 软件包Laravel 8 project composer require with all dependencies league flysystem
  • JAX-WS 客户端:访问本地 WSDL 的正确路径是什么?

    问题是我需要从我提供的文件构建一个 Web 服务客户端 我已将此文件存储在本地文件系统上 虽然我将 WSDL 文件保留在正确的文件系统文件夹中 但一切都很好 当我将其部署到服务器或从文件系统文件夹中删除 WSDL 时 代理找不到 WSDL
  • 在 git 中带注释的标签中输入“commit”

    在 git 中回显带注释的标签git cat file p
  • iOS 中的图像网格

    我想在 TabBarController 中创建一个 TabItem 其中包含用户可以选择的图像网格 我知道我可以使用 TableViewController 但这只会显示一长串列表 而不是 例如 3x4 图像的行 这可能吗 苹果有示例代码
  • Android 6.0+:使用新的 MIDI API 没有声音

    我正在使用新的 MIDI API为了播放一些 MIDI 音符 但是 我听不到任何声音 也没有抛出任何异常 其代码如下 initialising the MidiReceiver private MidiReceiver midiReceiv
  • 动态设置tableHeaderView高度

    我的应用程序创建一个 UITableViewController 其中包含一个可能具有任意高度的自定义 tableHeaderView 我一直在努力寻找动态设置此标头的方法 因为建议的方法似乎已经缩短了此标头 我的UITableViewCo
  • 对空值使用 join 方法的集合

    我有一个简单的方法 可以查看来自表单的请求参数并显示字符串中的值 此方法工作得很好 但是当表单中出现空值时 它会显示如下内容 beef mozzarella milk 您可以看到有一个额外的 如果该值为空 如何删除它 由于某种原因 检查 v
  • 如何测试 Spring Data 存储库?

    我想要一个存储库 比如说 UserRepository 在 Spring Data 的帮助下创建 我是 spring data 的新手 但不是 spring 我使用这个tutorial 我选择的处理数据库的技术是 JPA 2 1 和 Hib