简体   繁体   English

Spring Boot Data JPA 无法在 MockMVC 测试中自动装配存储库接口

[英]Spring Boot Data JPA cannot autowire repository interface in MockMVC test

I am using Spring Data JPA with Spring Boot and have a Mock MVC Test which results in the following stacktrace:我正在将 Spring Data JPA 与 Spring Boot 一起使用,并且有一个 Mock MVC 测试,它导致以下堆栈跟踪:

Parameter 1 of constructor in xyz.jacobclark.controllers.HomeController required a bean of type 'xyz.jacobclark.repositories.PageRepository' that could not be found.


Action:

Consider defining a bean of type 'xyz.jacobclark.repositories.PageRepository' in your configuration.

2017-02-26 18:29:11.350 ERROR 65426 --- [           main] o.s.test.context.TestContextManager      : Caught exception while allowing TestExecutionListener [org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@7219ec67] to prepare test instance [xyz.jacobclark.controllers.HomeControllerTest@1f0f1111]

java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124) ~[spring-test-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83) ~[spring-test-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:47) ~[spring-boot-test-autoconfigure-1.5.1.RELEASE.jar:1.5.1.RELEASE]
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230) ~[spring-test-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228) [spring-test-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287) [spring-test-4.3.6.RELEASE.jar:4.3.6.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:289) [spring-test-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247) [spring-test-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) [spring-test-4.3.6.RELEASE.jar:4.3.6.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-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) [spring-test-4.3.6.RELEASE.jar:4.3.6.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:191) [spring-test-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137) [junit-4.12.jar:4.12]
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117) [junit-rt.jar:na]
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42) [junit-rt.jar:na]
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262) [junit-rt.jar:na]
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84) [junit-rt.jar:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0]
    at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0]
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) [idea_rt.jar:na]
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'homeController' defined in file [/Users/jacobclark/workspace/jacobclark.xyz/build/classes/main/xyz/jacobclark/controllers/HomeController.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'xyz.jacobclark.repositories.PageRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:189) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1193) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1095) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:866) ~[spring-context-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542) ~[spring-context-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737) ~[spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:370) ~[spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:314) ~[spring-boot-1.5.1.RELEASE.jar:1.5.1.RELEASE]
    at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:120) ~[spring-boot-test-1.5.1.RELEASE.jar:1.5.1.RELEASE]
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98) ~[spring-test-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116) ~[spring-test-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    ... 28 common frames omitted
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'xyz.jacobclark.repositories.PageRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1486) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1104) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:835) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741) ~[spring-beans-4.3.6.RELEASE.jar:4.3.6.RELEASE]

Here is the actual repository:这是实际的存储库:

package xyz.jacobclark.repositories;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import xyz.jacobclark.models.entities.Page;

import java.util.List;

@Repository
public interface PageRepository extends CrudRepository<Page, String> {
    List<Page> findById(String id);

    List<Page> findByTitle(String titlex);
}

Whilst the controller looks like:虽然控制器看起来像:

package xyz.jacobclark.controllers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import xyz.jacobclark.adapters.GitHubAdapter;
import xyz.jacobclark.repositories.PageRepository;

import java.util.Map;
import java.util.concurrent.ExecutionException;

@Controller
public class HomeController {
    PageRepository pageRepository;
    private GitHubAdapter gitHubAdapter;

    @Autowired
    HomeController(GitHubAdapter gitHubAdapter, PageRepository pageRepository) {
        this.gitHubAdapter = gitHubAdapter;
        this.pageRepository = pageRepository;
    }

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String home(Map<String, Object> model) throws ExecutionException {
        model.put("githubRepoCount", gitHubAdapter.getGitHubRepositoriesCount());
        return "home";
    }
}

And finally, the failing test:最后,失败的测试:

package xyz.jacobclark.controllers;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import xyz.jacobclark.adapters.GitHubAdapter;

import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@RunWith(SpringRunner.class)
@WebMvcTest(HomeController.class)
public class HomeControllerTest {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private GitHubAdapter gitHubAdapter;

    @Test
    public void getOneRandomly() throws Exception {
        when(gitHubAdapter.getGitHubRepositoriesCount()).thenReturn("42");

        mvc.perform(get("/"))
                .andExpect(status().isOk())
                .andExpect(view().name("home"))
                .andExpect(model().attribute("githubRepoCount", "42"));
    }
}

According to the docs the annotation @WebMvcTest prevents components not relevant to MVC tests from being autowired, this includes @Repository , which is why the above code does not work.根据文档,注释@WebMvcTest可防止自动装配与 MVC 测试无关的组件,这包括@Repository ,这就是上述代码不起作用的原因。

http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/autoconfigure/web/servlet/WebMvcTest.html http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/autoconfigure/web/servlet/WebMvcTest.html

The error says: No qualifying bean of type 'xyz.jacobclark.repositories.PageRepository'错误说: No qualifying bean of type 'xyz.jacobclark.repositories.PageRepository'

You have Mocked GitHubAdapter but not provided any configuration to instantiate PageRepository .您已经 Mocked GitHubAdapter但没有提供任何配置来实例化PageRepository

I would suggest 3 options:我会建议 3 个选项:

  1. Mock PageRepository模拟PageRepository
  2. Provide a @ContextConfiguration to instantiate the bean提供@ContextConfiguration来实例化 bean
  3. Mark the repository as not required if null is ok: @Autowired(required = false)如果 null 正常,则将存储库标记为不需要: @Autowired(required = false)

Your Repository.您的存储库。

@Repository
public interface PageRepository extends CrudRepository<Page, String> {
    List<Page> findById(String id);

    List<Page> findByTitle(String titlex);
}

This @Repository(annotation) delete and move to your Implements(Implementation).这个@Repository(annotation) 删除并移动到你的Implements(Implementation)。 dao(interface) -> that annotation use DaoImpl class(implements). dao(interface) -> 该注解使用DaoImpl类(实现)。

Sample entityManagerFactory示例 entityManagerFactory

<jpa:repositories base-package="xyz.jacobclark.repositories"></jpa:repositories>

Sample Repository示例存储库

Don`t use Annotation(@Repository).不要使用注释(@Repository)。 you use base-package in settings config.您在设置配置中使用 base-package。

public interface AccountRepository extends PagingAndSortingRepository<Account, Integer>{

    public Account findById(String id);

    public List<Account> findByCompany(Company company);
}

Your controller您的控制器

@Controller
public class HomeController {
    PageRepository pageRepository;
    private GitHubAdapter gitHubAdapter;

    @Autowired
    HomeController(GitHubAdapter gitHubAdapter, PageRepository pageRepository) {
        this.gitHubAdapter = gitHubAdapter;
        this.pageRepository = pageRepository;
    }

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String home(Map<String, Object> model) throws ExecutionException {
        model.put("githubRepoCount", gitHubAdapter.getGitHubRepositoriesCount());
        return "home";
    }
}

Sample controller样品控制器

You use service and dao in controller.您在控制器中使用servicedao controller is gate.控制器是门。 so implementation is just bean.所以实现只是bean。 and i thinks you need to search DI and IOC.我认为您需要搜索 DI 和 IOC。 i recommend to don`t use your mock test.我建议不要使用你的模拟测试。 you use junit test.你使用junit测试。 and you call unit(service and dao).然后你打电话给单位(服务和道)。 this link : this Spring Tutorial and this jUnit + Spring Tutorial XD.此链接:此 Spring 教程此 jUnit + Spring 教程XD。

@RequestMapping(value = "/association")
@Controller
public class AssociationController {

    @Autowired
    private AssociationService associationService;

    @Autowired
    private AccountService accountService;

    @Autowired
    private CompanyService companyService;

    @Autowired
    private ProductService productService;

    @RequestMapping(value = "/settings/company")
    public ModelAndView showCompany(ModelAndView mav, HttpServletRequest req, Authentication auth) {
        CustomUserDetail customUserDetail = (CustomUserDetail) auth.getPrincipal();

        int associationIdx = customUserDetail.getAccount().getAssociation().getIdx();

        Association association = associationService.findAssociationByIdx(associationIdx);

        mav.addObject("association", association);
        mav.setViewName("/association/association_settings_company");

        return mav;
    }

    ....

}

For me this is what worked:对我来说,这是有效的:

  1. For any Spring-Data repository interface that needs mocking, use @MockBean .对于任何需要@MockBean Spring-Data 存储库接口,请使用@MockBean
  2. For any JPA repository implementation class, those can be mocked the standard way using @Mock (or with other standard methods)对于任何 JPA 存储库实现类,都可以使用@Mock (或其他标准方法)以标准方式@Mock
  3. Unit testing JPA+Spring-Data can be done without using a in-memory DB, and so don't assume you need one.单元测试 JPA+Spring-Data 可以在不使用内存数据库的情况下完成,所以不要假设你需要一个。
  4. The above advice will work if you have a service that uses a mixture of BOTH Spring-Data repository interfaces AND JPA implemented repository classes.如果您的服务混合使用 Spring-Data 存储库接口和 JPA 实现的存储库类,则上述建议将起作用。
  5. Instead of @WebMvcTest annotation, i use @DataJpaTest .我使用@DataJpaTest而不是@WebMvcTest注释。 Not everyone needs to use @WebMvcTest.不是每个人都需要使用@WebMvcTest。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM