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

为什么我的org.springframework.test.web.servlet.MockMvc框架无法解析控制器的构造函数参数

澹台展鹏
2023-03-14

我想为我的Spring Boot应用程序创建一个单元测试,但在启动一个UserControllerTest测试时,我收到以下错误:

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in com.sample.order.OrderController required a bean of type 'com.sample.order.OrderRepository' that could not be found.


Action:

Consider defining a bean of type 'com.sample.order.OrderRepository' in your configuration.

当我试图从UserControllerTest运行测试时,OrderController无法满足构造函数参数,反之亦然。

该应用程序很简单,只有两个控制器,用于管理订单和用户。源代码具有以下结构:

src/
├── main
│   ├── kotlin
│   │   └── com
│   │       └── sample
│   │           ├── OrderApplication.kt
│   │           ├── order
│   │           │   ├── Order.kt
│   │           │   ├── OrderAssembler.kt
│   │           │   ├── OrderController.kt
│   │           │   └── OrderRepository.kt
│   │           └── user
│   │               ├── User.kt
│   │               ├── UserAssembler.kt
│   │               ├── UserController.kt
│   │               └── UserRepository.kt
│   └── resources
│       └── application.properties
└── test
    └── kotlin
        └── com
            └── sample
                ├── order
                │   └── OrderControllerTest.kt
                └── user
                    └── UserControllerTest.kt

一般来说,为了构建我的应用程序,我试着遵循Spring Boot kotlin教程。

应用程序本身运行没有任何问题。问题在于运行测试时。

我看到框架提出的行动相当明显:

考虑定义一个'com'类型的bean。样品顺序配置中的OrderRepository。

,但我为什么要定义'com'类型的bean呢。样品顺序OrderRepository'在html" target="_blank">配置中?当应用程序本身工作并且不需要任何配置时,测试不应该开箱即用吗?除此之外,OrderRepository是一种接口类型:

interface OrderRepository: CrudRepository<Order, Long>

当Spring施展魔法时,我不想提供它的实例化。

我尝试的是:

发件人:

class OrderController(private val repository: OrderRepository
              ,private val assembler: OrderAssembler) {
...

到:

class OrderController(private val repository: OrderRepository? = null
              ,private val assembler: OrderAssembler? = null) {
...

它解决了这个问题,但出于明显的原因,我不想有这个解决方案。

class UserControllerTest(@Autowired val mockMvc: MockMvc) {

    @MockkBean
    private lateinit var userRepository: UserRepository

    @SpykBean
    private lateinit var userAssembler: UserAssembler

    // !!!!!! NOTE: orderRepository inside user tests ¯\_(ツ)_/¯ 
    @MockkBean
    private lateinit var orderRepository: OrderRepository

    // !!!!!! NOTE: orderAssembler inside user tests ¯\_(ツ)_/¯ 
    @SpykBean
    private lateinit var orderAssembler: OrderAssembler
    

    @Test
    fun `Should form expected json response with user data`(){
        val user = User(id = 5, login="john.doe@aol.com", firstname = "John", lastname = "Doe")
        every { userRepository.findAll() } returns listOf(user)
        mockMvc.perform(get("/api/v1/users/").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk)
    }
}

它也解决了这个问题,但这是一个丑陋的解决办法。

@SpringBootApplication
@ComponentScan("com.sample.order")
class OrderApplication

fun main(args: Array<String>) {
    runApplication<OrderApplication>(*args)
}

这并不能解决问题。

UserController的代码:

@RestController
class UserController(private val repository: UserRepository,
                     private val assembler: UserAssembler) {

    @GetMapping("/api/v1/user/{id}")
    fun findOne(@PathVariable id: Long): EntityModel<User> {
        val user = repository.findById(id)
        if (user.isPresent) {
            return assembler.toModel(user.get())
        }
        throw ResponseStatusException(HttpStatus.NOT_FOUND, "This user does not exist")
    }

    @GetMapping("/api/v1/users")
    fun all(): CollectionModel<EntityModel<User>> {
        val users = repository.findAll().map {
            assembler.toModel(it)
        }
        return CollectionModel.of(users,  //
                linkTo<UserController>{all()}.withRel("all"))
    }
}

这没用。除此之外,我的应用程序可以毫无问题地运行,所以我认为组件扫描不是一个原因。

使用的版本:

  • 爪哇:14

下面是完整的调用堆栈:

Failed to resolve parameter [org.springframework.test.web.servlet.MockMvc mockMvc] in constructor [public com.sample.user.UserControllerTest(org.springframework.test.web.servlet.MockMvc)]
org.junit.jupiter.api.extension.ParameterResolutionException: Failed to resolve parameter [org.springframework.test.web.servlet.MockMvc mockMvc] in constructor [public com.sample.user.UserControllerTest(org.springframework.test.web.servlet.MockMvc)]
    at org.junit.jupiter.engine.execution.ExecutableInvoker.resolveParameter(ExecutableInvoker.java:221)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.resolveParameters(ExecutableInvoker.java:174)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.resolveParameters(ExecutableInvoker.java:135)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:61)
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.invokeTestClassConstructor(ClassTestDescriptor.java:342)
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.instantiateTestClass(ClassTestDescriptor.java:289)
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.instantiateTestClass(ClassTestDescriptor.java:281)
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.instantiateAndPostProcessTestInstance(ClassTestDescriptor.java:269)
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.lambda$testInstanceProvider$2(ClassTestDescriptor.java:259)
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.lambda$testInstanceProvider$3(ClassTestDescriptor.java:263)
    at java.base/java.util.Optional.orElseGet(Optional.java:369)
    at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.lambda$testInstanceProvider$4(ClassTestDescriptor.java:262)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:82)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:59)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$1(NodeTestTask.java:111)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:111)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:79)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$5(DefaultLauncher.java:211)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132)
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99)
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:79)
    at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:75)
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
    at com.sun.proxy.$Proxy2.stop(Unknown Source)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.stop(TestWorker.java:133)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182)
    at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164)
    at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:414)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:123)
    at org.springframework.test.context.junit.jupiter.SpringExtension.getApplicationContext(SpringExtension.java:271)
    at org.springframework.test.context.junit.jupiter.SpringExtension.resolveParameter(SpringExtension.java:257)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.resolveParameter(ExecutableInvoker.java:207)
    ... 74 more
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderController' defined in file [.../webapp/build/classes/kotlin/main/com/sample/order/OrderController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.sample.order.OrderRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:229)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1356)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1206)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:571)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:531)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:923)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:588)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:767)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:426)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:326)
    at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:122)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
    ... 78 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.sample.order.OrderRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1777)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1333)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1287)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
    ... 97 more

测试源代码:

  1. OrderControlllerTest.kt
package com.sample.order

import com.ninjasquad.springmockk.MockkBean
import com.ninjasquad.springmockk.SpykBean
import io.mockk.every
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import java.util.*

@WebMvcTest
class OrderControllerTest(@Autowired val mockMvc: MockMvc) {

    //Why we need that to have test compilable ?
    /*
    @MockkBean
    private lateinit var userRepository: UserRepository

    @SpykBean
    private lateinit var userAssembler: UserAssembler
    */

    @MockkBean
    private lateinit var orderRepository: OrderRepository

    @SpykBean
    private lateinit var orderAssembler: OrderAssembler

    @Test
    fun `Should form expected json response with order data`(){
        val order = Order(id = 7, date = 1501212321)
        every { orderRepository.findById(any()) } returns Optional.of(order)
        mockMvc.perform(get("/api/v1/order/7").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk)
                .andExpect(jsonPath("$.id").value(order.id))
    }
}
package com.sample.user

import com.ninjasquad.springmockk.MockkBean
import com.ninjasquad.springmockk.SpykBean
import io.mockk.every
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status

@WebMvcTest
class UserControllerTest(@Autowired val mockMvc: MockMvc) {

    @MockkBean
    private lateinit var userRepository: UserRepository

    @SpykBean
    private lateinit var userAssembler: UserAssembler

    /*
    @MockkBean
    private lateinit var orderRepository: OrderRepository

    @SpykBean
    private lateinit var orderAssembler: OrderAssembler
    */

    @Test
    fun `Should form expected json response with user data`(){
        val user = User(id = 5, login = "john.doe@aol.com", firstname = "John", lastname = "Doe")
        every { userRepository.findAll() } returns listOf(user)
        mockMvc.perform(get("/api/v1/users").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk)
    }
}

源代码可在github上获得

共有1个答案

贝嘉泽
2023-03-14

解决方案是更改测试注释

  • 添加@AutoConfigureMockMvc

工作测试注释如下所示:

@AutoConfigureMockMvc
@SpringBootTest
//@WebMvcTest
class UserControllerTest(@Autowired val mockMvc: MockMvc) {
    ...

@AutoConfigureMockMvc-启用和配置MockMvc的自动配置

@SpringBootTest-告诉Spring Boot去寻找一个主配置类(例如带有@SpringBootApplication的配置类),并用它来启动Spring应用程序上下文。SpringBootTest加载完整的应用程序并注入所有可能很慢的bean。

@WebMvcTest-用于测试控制器层,需要使用模拟对象提供所需的其余依赖项。

更多详细信息可在此处找到:

  • 其他问题
 类似资料:
  • 问题内容: 大家好,我正在尝试在选项卡中列出文本数据,这是我的方法 无法解析构造函数ArrayAdapter 的方法,这是我的主要活动。任何帮助将不胜感激,我仍然是一个noobie 。 问题答案: 更改 至 你可以在使用。

  • 问题内容: 我的一项作业需要一个银行帐户才能从支票和储蓄帐户转帐资金。交易存储在ArrayList中,并设置为用户指定何时转移资金。用于支票和储蓄的银行帐户类可以正常工作,但是我创建的TransferService类在NetBeans中不能正确编译。 这些提示似乎无法解决错误。我得到错误: 事务是抽象的,无法实例化。 我该如何解决这个问题? 问题答案: 构造函数没有返回类型。所以不 反而 关于,

  • 问题内容: 有人可以帮我这段代码。当前它将在第4行进行投诉:webDriver =新的FirefoxDriver(ff_ep_profiles)说它无法解析构造函数。我需要加载扩展程序,因此我正在创建配置文件 问题答案: 在使用 Selenium v​​3.11.x , GeckoDriver v0.20.0 和 Firefox Quantum v59.0.2时 ,可以使用不同的选项来调用新的/现

  • 当Java中给出参数构造函数时,为什么默认的无参数构造函数会失败? 这个设计有什么意义? 例如:

  • 问题内容: 根据Java语言规范,无法将构造函数标记为已同步,因为其他线程在创建该对象的线程完成之前无法看到正在创建的对象。这似乎有些奇怪,因为在构造对象时,我确实可以让另一个线程查看该对象: 我知道这是一个非常人为的示例,但从理论上讲,似乎有人可以提出一个更现实的案例,在该案例中,标记构造函数为同步状态是合法的,以防止此类线程的竞争。 我的问题是:Java是否有理由特别禁止在构造函数上使用syn

  • 本文向大家介绍请解释Java中的概念,什么是构造函数?什么是构造函数重载?什么是复制构造函数?相关面试题,主要包含被问及请解释Java中的概念,什么是构造函数?什么是构造函数重载?什么是复制构造函数?时的应答技巧和注意事项,需要的朋友参考一下 考察点:JAVA构造函数 当新对象被创建的时候,构造函数会被调用。每一个类都有构造函数。在程序员没有给类提供构造函数的情况下,Java编译器会为这个类创建一