当前位置: 首页 > 知识库问答 >
问题:

对Spring数据JPA@Query进行单元测试是不可能的,真的吗?

云欣嘉
2023-03-14

首先,目标。什么是单元测试?单元测试是测试最小功能的测试,与测试更多功能的集成测试相反,例如:

  • 以任何方式或任何形式生成产品ApplicationContext的测试都不是单元测试;
  • 触及或甚至知道应用程序(标记为@SpringBootApplication)类的测试不是单元测试;
  • src/main/resources加载内容的测试不是单元测试
  • 从Spring Cloud Config server加载外部配置测试绝对不是单元测试;
  • 存储库的单元测试不能启动(甚至不知道)web、mvc或安全问题。

那么,如何为Spring数据JPA存储库编写单元测试呢?(或者这么受欢迎和喜爱的框架不支持这种东西的纯单元测试?)

我的项目:Spring Cloud(Cloud Counfig service,Security OAuth2 service,eureka,zuul,authentication,authorization等)

让我们尝试测试最简单的存储库:

public interface StudentRepository extends CrudRepository<Student, Integer> {
    Optional<Student> findByStudentCode(Integer studentCode);
    Optional<Student> findTopByOrderByStudentCodeDesc();

    @Query(value = "SELECT COUNT(*) = 0 FROM t_student WHERE regexp_replace(LOWER(student_name), '\\s', '', 'g') = regexp_replace(LOWER(:suspect), '\\s', '', 'g')", nativeQuery = true)
    boolean isStudentNameSpeciallyUnique(@Param("suspect") String studentName);
}

学生实体将具有:id、代码(自然id)、姓名、年龄。没什么特别的.这就是测试。我们需要一个SUT(我们的存储库)和实体管理器来预填充SUT。所以:

@RunWith(SpringRunner.class)
@DataJpaTest // <-- loads full-blown production app context and fails!
public class StudentRepositoryTest {

    @Autowired
    TestEntityManager manager;

    @Autowired
    StudentRepository repository;

    @Test
    public void findByStudentCode_whenNoSuch_shouldReturnEmptyOptional() {
        final int invalidStudentCode = 321;
        Optional<Student> student = repository.findByStudentCode(invalidStudentCode);
        assertFalse(student.isPresent());
    }
}

运行它会产生以下结果:

...
2019-01-01 18:32:10.750  INFO 15868 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@2d72f75e: startup date [Tue Jan 01 18:32:10 EET 2019]; root of context hierarchy
2019-01-01 18:32:11.294  INFO 15868 --- [           main] f.a.AutowiredAnnotationBeanPostProcessor : JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
2019-01-01 18:32:11.421  INFO 15868 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'configurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$9ed1a748] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.6.RELEASE)

2019-01-01 18:32:30.694  INFO 15868 --- [           main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://localhost:8888\
...

@datajpatest向上搜索包的层次结构并加载生产应用程序:

package org.givespring.atry.university.studentservice;
@EnableResourceServer
@EnableJpaAuditing
@SpringBootApplication(scanBasePackages = "org.givespring.atry.university") // to discover MicroserviceProperties and custom usercontext stuff
public class StudentServiceApp {
    public static void main(String[] args) {
        SpringApplication.run(StudentServiceApp.class, args);
    }
}

因此,错误为:

2019-01-01 18:32:32.789 ERROR 15868 --- [           main] o.s.boot.SpringApplication               : Application run failed

java.lang.IllegalStateException: Unable to retrieve @EnableAutoConfiguration base packages
    at org.springframework.boot.autoconfigure.AutoConfigurationPackages.get(AutoConfigurationPackages.java:76) ~[spring-boot-autoconfigure-2.0.6.RELEASE.jar:2.0.6.RELEASE]
    at org.springframework.boot.autoconfigure.data.AbstractRepositoryConfigurationSourceSupport.getBasePackages(AbstractRepositoryConfigurationSourceSupport.java:79) ~[spring-boot-autoconfigure-2.0.6.RELEASE.jar:2.0.6.RELEASE]
    at org.springframework.boot.autoconfigure.data.AbstractRepositoryConfigurationSourceSupport$1.getBasePackages(AbstractRepositoryConfigurationSourceSupport.java:73) ~[spring-boot-autoconfigure-2.0.6.RELEASE.jar:2.0.6.RELEASE]
    at org.springframework.data.repository.config.RepositoryConfigurationSourceSupport.lambda$getCandidates$2(RepositoryConfigurationSourceSupport.java:77) ~[spring-data-commons-2.0.11.RELEASE.jar:2.0.11.RELEASE]
    at org.springframework.data.util.LazyStreamable.stream(LazyStreamable.java:50) ~[spring-data-commons-2.0.11.RELEASE.jar:2.0.11.RELEASE]
    at org.springframework.data.util.LazyStreamable.iterator(LazyStreamable.java:41) ~[spring-data-commons-2.0.11.RELEASE.jar:2.0.11.RELEASE]
    at org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport.getRepositoryConfigurations(RepositoryConfigurationExtensionSupport.java:87) ~[spring-data-commons-2.0.11.RELEASE.jar:2.0.11.RELEASE]
    at org.springframework.data.repository.config.RepositoryConfigurationDelegate.registerRepositoriesIn(RepositoryConfigurationDelegate.java:126) ~[spring-data-commons-2.0.11.RELEASE.jar:2.0.11.RELEASE]
    at org.springframework.boot.autoconfigure.data.AbstractRepositoryConfigurationSourceSupport.registerBeanDefinitions(AbstractRepositoryConfigurationSourceSupport.java:60) ~[spring-boot-autoconfigure-2.0.6.RELEASE.jar:2.0.6.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.lambda$loadBeanDefinitionsFromRegistrars$1(ConfigurationClassBeanDefinitionReader.java:358) ~[spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at java.util.LinkedHashMap.forEach(LinkedHashMap.java:684) ~[na:1.8.0_121]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:357) ~[spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:146) ~[spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:118) ~[spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:328) ~[spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:233) ~[spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:271) ~[spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:91) ~[spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:692) ~[spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:530) ~[spring-context-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-2.0.6.RELEASE.jar:2.0.6.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:386) ~[spring-boot-2.0.6.RELEASE.jar:2.0.6.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) ~[spring-boot-2.0.6.RELEASE.jar:2.0.6.RELEASE]
    at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:127) [spring-boot-test-2.0.6.RELEASE.jar:2.0.6.RELEASE]
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99) [spring-test-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117) [spring-test-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108) [html" target="_blank">spring-test-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117) [spring-test-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83) [spring-test-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:44) [spring-boot-test-autoconfigure-2.0.6.RELEASE.jar:2.0.6.RELEASE]
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:246) [spring-test-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227) [spring-test-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289) [spring-test-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291) [spring-test-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246) [spring-test-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) [spring-test-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) [spring-test-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190) [spring-test-5.0.10.RELEASE.jar:5.0.10.RELEASE]
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137) [junit-4.12.jar:4.12]
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) [junit-rt.jar:na]
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) [junit-rt.jar:na]
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) [junit-rt.jar:na]
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) [junit-rt.jar:na]

2019-01-01 18:32:32.792  INFO 15868 --- [           main] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@6a2eea2a: startup date [Tue Jan 01 18:32:32 EET 2019]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@2d72f75e

共有1个答案

孟建木
2023-03-14

简而言之,单元测试是对小单元的快速测试。什么是单元取决于开发人员或架构指南。它可以是任何小的、可测试的代码片段,如方法或类。

此外,如果您仔细考虑一下,您没有为存储库编写代码。你依赖于Spring平台。如果你发现了一些错误,请随便开罚单

因此不需要为Spring Data JPA存储库方法编写任何单元测试。

如果您需要为一个Spring数据JPA存储库编写单元测试,我假设您关心的问题与方法有关:save、delete、findOne、findAll等,在这种情况下,正如您所看到的,这不小,因此需要进行集成测试。

这个存储库包含一个使用spring jpa存储库方法进行集成测试的示例:

https://github.com/jrichardsz/spring-boot-templates/blob/master/003-hql-database-with-integration-test/src/test/java/test/customerrepositoryintegrationtest.java

参考资料:

  • https://stackoverflow.com/A/23442457/3957754
  • https://www.quora.com/What-is-the-real-efficiency-of-unit-testing
 类似资料:
  • 然而,该项目有许多类似的“测试”: 这个特殊的类创建了一个示例数据库映像,供开发人员使用。诚然,我们可以创建直接的SQL脚本,但Java进程非常有用,因为其中的代码也可以从外部源(例如Liferay)查询数据 我们为此使用单元测试的原因是因为开发人员可以轻松地在IntelliJ中运行它来加载新的数据库映像。然而,这并不是真正的“测试”,而是使用测试运行器作为运行Java进程的快速方法。 我正在建立

  • 我有一个配置了JPA的Spring Boot应用程序。在查询数据库时,我在spring data@query注释的帮助下运行本机sql查询。现在我正计划使用Spring Boot测试框架编写测试用例。是否可以编写测试? 代码: 测试案例: 错误: 在org.springframework.transaction.interceptor.transactionaspectsupport.java:3

  • 我有一个Spring Data JPA存储库,只要不添加Spring Security性依赖项(spring-boot-starter-security)并在存储库上添加相应的方法授权注释,单元测试就可以正常工作。添加后,在运行单元测试时,我会得到一个AuthenticationCredentialsNotFound异常。 如何在单元测试中“验证”对存储库方法的调用?

  • 问题内容: 如果是跑它运行在您的文件结尾通过运行启动格式的功能和使用(* T testing.T)模块。我想知道文件中的每个功能是同时运行还是确定地分别运行每个功能?是否为每个人创建一个执行例程?如果确实为每个例程创建了一个go例程,是否可以某种方式监视go例程?是否有可能做类似的事情并为每个实例获取一个实例并对其进行监控,诸如此类? 注意:这个问题假设您使用go(测试)随附的测试框架。 问题答案

  • 问题内容: 我有几个DAO对象,这些对象用于从数据库中检索信息,我 确实 想为它们编写一些自动化测试,但是我很难弄清楚该怎么做。 我正在使用Spring 来运行实际查询(通过准备好的语句)并将结果映射到模型对象(通过类)。 如果要编写单元测试,则不确定如何/应该模拟对象。例如,由于只有读操作,因此我将使用实际的数据库连接,而不模拟jdbcTemplate,但我不确定那是正确的。 这是批处理中最简单

  • 本文向大家介绍对 Vue-Router 进行单元测试的方法,包括了对 Vue-Router 进行单元测试的方法的使用技巧和注意事项,需要的朋友参考一下 由于路由通常会把多个组件牵扯到一起操作,所以一般对其的测试都在 端到端/集成 阶段进行,处于测试金字塔的上层。不过,做一些路由的单元测试还是大有益处的。 对于与路由交互的组件,有两种测试方式: 使用一个真正的 router 实例 mock 掉 $r