Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >如何测试Spring数据存储库?

如何测试Spring数据存储库?
EN

Stack Overflow用户
提问于 2014-05-02 11:54:48
回答 11查看 267K关注 0票数 181

我希望在Spring的帮助下创建一个存储库(比如UserRepository)。我对spring数据(但不是spring)很陌生,我使用这个教程。我选择的处理数据库的技术是JPA2.1和Hibernate。问题是,对于如何为这样的存储库编写单元测试,我一无所知。

让我们以create()方法为例。当我在进行测试时--首先,我应该为它编写一个单元测试--这就是我遇到三个问题的地方:

  • 首先,如何将EntityManager的模拟注入到UserRepository接口的不存在实现中?Spring数据将根据这个接口生成一个实现: 公共接口UserRepository扩展CrudRepository {} 但是,我不知道如何强制它使用EntityManager模拟和其他模拟--如果我自己编写了实现,我可能会有一个用于EntityManager的setter方法,允许我在单元测试中使用模拟。(至于实际的数据库连接,我有一个用@Configuration@EnableJpaRepositories注释的@Configuration类,它以编程方式为DataSourceEntityManagerFactoryEntityManager等定义beans -但是存储库应该是测试友好的,并且允许覆盖这些东西)。
  • 第二,我应该测试交互吗?我很难弄清楚EntityManagerQuery的哪些方法应该被调用(类似于verify(entityManager).createNamedQuery(anyString()).getResultList();),因为编写实现的不是我。
  • 第三,我应该首先对Spring生成的方法进行单元测试吗?据我所知,第三方库代码不应该进行单元测试--只有开发人员自己编写的代码才应该进行单元测试。但是如果是这样的话,它仍然会把第一个问题带回场景:比如说,我为我的存储库提供了一些自定义方法,我将为此编写实现,如何将EntityManagerQuery的模拟注入到最终生成的存储库中?

注意:我将测试-使用集成和单元测试来驱动我的存储库。对于我的集成测试,我使用的是内存中的HSQL数据库,显然我没有使用数据库进行单元测试。

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

更新:今天,我继续对模拟注入进行实验--我创建了一个静态内部类以允许模拟注入。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@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());
}

}

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
EN

回答 11

Stack Overflow用户

回答已采纳

发布于 2014-05-03 01:16:34

tl;dr

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

详细信息

我们做了相当多的前期验证和设置,以确保您只能引导一个应用程序,没有无效的派生查询等。

  • 我们为派生查询创建和缓存CriteriaQuery实例,以确保查询方法不包含任何类型的输入。这需要使用标准API以及meta.model。
  • 我们通过请求EntityManager为这些查询创建一个Query实例来验证手动定义的查询(这有效地触发了查询语法验证)。
  • 我们检查Metamodel中关于处理域类型的元数据,准备is-新检查等等。

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

考虑一下,您没有为存储库编写代码,因此没有必要编写任何_unit_tests。根本没有必要,因为您可以依靠我们的测试库来捕获基本的bug(如果您仍然遇到一个bug,可以随意地引发一个票证)。但是,肯定需要集成测试来测试持久化层的两个方面,因为它们是与您的域相关的方面:

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

集成测试

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

测试自定义实现

存储库的自定义实现部分是以某种方式写成,它们不需要了解Spring。它们是普通的Spring,会被注入EntityManager。当然,你可能想试着嘲笑它的交互,但老实说,对我们来说,对JPA进行单元测试并不是一种令人愉快的体验,而且它与相当多的间接(EntityManager,->,CriteriaBuilderCriteriaQuery等)一起工作。这样,您就可以得到模拟、返回的模拟等等。

票数 153
EN

Stack Overflow用户

发布于 2017-04-25 04:28:58

对于Spring + Spring数据,它变得非常容易:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@RunWith(SpringRunner.class)
@DataJpaTest
public class MyRepositoryTest {

    @Autowired
    MyRepository subject;

    @Test
    public void myTest() throws Exception {
        subject.save(new MyEntity());
    }
}

@heez的解决方案提供了完整的上下文,这只会带来JPA+Transaction工作所需的内容。注意,上面的解决方案将弹出内存测试数据库,因为可以在类路径上找到一个数据库。

票数 68
EN

Stack Overflow用户

发布于 2015-02-20 22:46:14

这可能来得太晚了,但我已经为此目的写了一些东西。我的库将为您模拟基本的crud存储库方法,并解释查询方法的大部分功能。您必须为您自己的本机查询注入功能,但其余的功能都是为您完成的。

看一看:

https://github.com/mmnaseri/spring-data-mock

更新

这是现在的Maven中心和相当好的状态。

票数 28
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23435937

复制
相关文章
屏幕坐标与客户坐标的区别
屏幕坐标是相对于屏幕左上角(0,0)位置的坐标,客户坐标是相对于某个窗口客户区左上角的坐标,当谈到客户坐标时需要说明是相对哪个窗口的客户坐标。
gaigai
2019/09/29
2.2K0
屏幕坐标与客户坐标的区别
Unity ugui屏幕适配与世界坐标到ugui屏幕坐标的转换
我们知道,如今的移动端设备分辨率五花八门,而开发过程中往往只取一种分辨率作为设计参考,例如采用1920*1080分辨率作为参考分辨率。
汐夜koshio
2020/03/19
2.9K0
【Android 屏幕适配】屏幕适配基础概念 ② ( 像素 px 与 密度无关像素 dip | 像素 px 与 密度无关像素 dip 在不同屏幕像素密度 dpi 下的换算关系 )
dip 是 Desity Independent Pixels 的缩写 , 表示 密度无关像素 , dip 又可以缩写成 dp ;
韩曙亮
2023/03/30
2K0
VC如何获取对话框中控件的坐标
VC如何获取对话框中控件的坐标 GetWindowRect是取得窗口在屏幕坐标系下的RECT坐标(包括客户区和非客户区),这样可以得到窗口的大小和相对屏幕左上角(0,0)的位置。 GetClientRect取得窗口客户区(不包括非客户区)在客户区坐标系下的RECT坐标,可以得到窗口的大小,而不能得到相对屏幕的位置,它的top和left都为0,right和botton是宽和高,因为这个矩阵是在客户区坐标系下(相对于窗口客户区的左上角)的。   ClientToScreen把客户区坐标系下的RECT坐标转换为屏
_gongluck
2018/03/08
2.6K0
屏幕坐标系和常用UI坐标系的转换
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
bering
2019/12/02
2.3K0
球心坐标与本地坐标
1球心坐标(ECEF)与本地坐标(NEU) 假如你来到一个陌生城市,你很可能需要问路、通常会告诉你向北走100米,右转,向东走100米,理解起来很直观。你给儿子买了一个地球仪,你从北京(39,115)转到伦敦 (51,0),这个动作就可以分解为两步:先转到同一个经度(39,0),在转到同一个维度(51,0) 这个例子体现了一个问题:不同的地理范围下会使用更适合的坐标系。比如前者是局部的平面坐标,而后者是球面坐标。因此,同一个点相对不同的原点,具有不同的相对位置:既是地球上的一个经纬度,又是“出门右转富士康
Peter Lu
2018/06/20
2.3K0
【Android 应用开发】Android 组件 位置坐标 属性 ( 组件位置属性 | 父容器坐标系坐标 | 窗口坐标系坐标 | 屏幕坐标系坐标 | 触摸坐标 )
left , top , right , bottom 是组件相对于父容器的位置 , 该值一般不会改变 ;
韩曙亮
2023/03/27
4K0
坐标系与矩阵(4):球心坐标与NEU坐标系
前三篇介绍了坐标系和矩阵的数学知识,从本篇开始,我们试图运用这些知识来解决实际问题。
Peter Lu
2021/07/20
3.4K0
坐标系与矩阵(4):球心坐标与NEU坐标系
【Unity3D】实现屏幕坐标和3维空间坐标的转化
[这里写图片描述] Input.mousePosition就是鼠标所在的位置的坐标 函数Camera.main.ScreenToWorldPoint就是屏幕坐标转化三维空间的函数 Camera.main.ScreenToWorldPoint [这里写图片描述] 把脚本挂载在主摄像机上 运行 [这里写图片描述] 就得到屏幕坐标映射在三维世界中的坐标了 不过有一点是,转化之后z轴是-10 这个时候只要z轴+10就是屏幕映射到三维世界的正确坐标了 Vector3 mousePos = Camera.main.Sc
恬静的小魔龙
2020/03/10
1.7K0
Threejs入门之二十二:Threejs中的屏幕坐标转标准设备坐标
在上一节中,我们在监听鼠标移动事件时,将其坐标范围处理为了[-1,1]的范围,使用如下代码
九仞山
2023/04/30
2.3K0
Threejs入门之二十二:Threejs中的屏幕坐标转标准设备坐标
Android获得控件在屏幕中的绝对坐标
计算该视图在全局坐标系中的x,y值,(注意这个值是要从屏幕顶端算起,也就是索包括了通知栏的高度)//获取在当前屏幕内的绝对坐标
他叫自己MR.张
2019/07/01
2.1K0
ThreeJS中三维世界坐标转换成二维屏幕坐标
Threejs全称是“Javascript 3D library”。WebGL是openGL在浏览器上的一个实现。Threejs对WebGL进行了封装,让前端开发人员在不需要掌握很多数学知识和绘图知识的情况下轻松进行web 3D开发,简单易用。
程序你好
2021/07/23
5.2K0
ThreeJS中三维世界坐标转换成二维屏幕坐标
【Android 屏幕适配】屏幕适配通用解决方案 ② ( 自定义组件解决方案 | 需要解决的问题 : 设计稿坐标数据转为屏幕真实坐标数据 | 实现步骤 )
使用的 dimens.xml 配置的方式 实现 屏幕适配 , 在 开发时 就 事先将对应屏幕分辨率的值换算好 并 配置到 dimens.xml 文件 中 , 在程序运行时只需要直接调用即可 , 不需要消耗资源进行额外的计算 ;
韩曙亮
2023/03/30
4590
与代码无关的网络安全
实际上,真实世界中的网络安全往往致力于解决非代码的漏洞,也就是说,除了传统的计算机网络安全之外,还会涉及到网络安全的管理、政策、法律和国际事务。借鉴于我们所熟知的OSI 7层协议模型,可以在之上增加组织、政府和国际事务的新分层,从而可以对与代码无关的网络安全问题进行分类,进而提出应对措施。
半吊子全栈工匠
2022/01/24
3280
与代码无关的网络安全
【100个 Unity实用技能】| Unity InputSystem中拿到触摸屏幕的坐标,鼠标的坐标等
在Unity的新输入系统InputSystem中,获取键盘鼠标的API发生了变化,不再是之前用Input.就可以拿到了。
呆呆敲代码的小Y
2022/10/05
3.3K1
【100个 Unity实用技能】| Unity InputSystem中拿到触摸屏幕的坐标,鼠标的坐标等
minigui/mgncs:双缓冲区实现窗口推拉特效
版权声明:本文为博主原创文章,转载请注明源地址。 https://blog.csdn.net/10km/article/details/84951442
10km
2019/05/25
1.3K0
1341 与3和5无关的数
1341 与3和5无关的数 时间限制: 1 s 空间限制: 64000 KB 题目等级 : 白银 Silver 题目描述 Description 有一正整数a,如果它能被x整除,或者它的十进制表示法中某位上的数字为x,则称a与x相关.现求所有小于等于n的与3或5无关的正整数的平方和. 输入描述 Input Description 只有一行,一个正整数n(0<n<300) 输出描述 Output Description 只有一行,小于等于n的与3和5无关的正整数的平方和 样例输入 Sample
attack
2018/04/13
5750
坐标转换与姿态描述
为了能够科学的反映物体的运动特性,会在特定的坐标系中进行描述,一般情况下,分析飞行器运动特性经常要用到以下几种坐标系统1、大地坐标系统;2、地心固定坐标系统;3、本地北东地坐标系统;4、机载北东地坐标系统;5、机体轴坐标系统。 其中3、4、5在我们建模、设计控制律时都是经常需要使用的坐标系,描述物体(刚体)位姿信息的6个自由度信息都是在这三个坐标系中产生的
小飞侠xp
2019/10/13
2.5K0
Cocos Creator 坐标与转换
node.position是本地坐标,也就是在父节点中的坐标。 让父节点调用convertToWorldSpaceAR方法,把节点的本地坐标转换为世界坐标。
AnRFDev
2021/02/01
1.7K0
点击加载更多

相似问题

GetWindowRect返回错误坐标

28

Qt grabWindow坐标从GetCursorPos和GetWindowRect移动

21

SVG -与形状坐标无关的线性梯度

11

画布坐标与屏幕坐标的关系

10

引导列跨度与屏幕大小无关

10
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文