【问题标题】:Unable to mock Spring-Data-JPA repositories with Mockito无法使用 Mockito 模拟 Spring-Data-JPA 存储库
【发布时间】:2016-06-06 11:11:03
【问题描述】:

我正在尝试为服务编写测试。但我没有成功模拟 repository 依赖项。其他非存储库依赖项已成功模拟。存储库实例始终是实际实现,而不是模拟实例。

我正在使用 Spring Boot 和 Spring Data JPA 来构建应用程序。 Mockito 用于模拟。我设法将问题提炼到一个测试项目中。完整的测试项目位于GitHub。以下是来自测试项目的代码的 sn-ps;以下是PersonServiceTest 类。

更新 1: before() 代码应该检查 personRepository 而不是 personService

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(App.class)
@WebAppConfiguration
@TestExecutionListeners({ServletTestExecutionListener.class, DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class, SqlScriptsTestExecutionListener.class})
@Transactional
@ActiveProfiles({"mock-some-bean", "mock-person-repository"})
public class PersonServiceTest {

    @Inject
    private SomeBean someBean;
    @Inject
    private PersonRepository personRepository;
    @Inject
    private PersonService personService;

    @Before
    public void before() {
        assertThat(mockingDetails(someBean).isMock(), is(true));
        assertThat(mockingDetails(personRepository).isMock(), is(true));
    }

    @Test
    public void doSomething() throws Exception { ... }
}

测试类使用两个配置文件:mock-some-beanmock-person-repository。基本上,我使用配置文件来确定应该模拟什么。在进行任何测试之前,我断言 someBeanpersonService 是模拟实例。 someBean 被正确模拟,但 personService 总是失败。以下代码是来自TestConfig 类的代码。

@Configuration
public class TestConfig {

    private static final Logger logger = LoggerFactory.getLogger(TestConfig.class);

    @Bean
    @Profile("mock-some-bean")
    public SomeBean someBean() {
        logger.info("Mocking: {}", SomeBean.class);
        return mock(SomeBean.class);
    }

    @Bean
    @Profile("mock-person-repository")
    public PersonRepository personRepository() {
        logger.info("Mocking: {}", PersonRepository.class);
        return mock(PersonRepository.class);
    }
}

更新 2: 问题更清晰

我错过了什么? Spring Data JPA 似乎总是创建一个实例并忽略TestConfig 类中定义的@Bean。我如何“告诉” Spring Data JPA 不要创建实例?感谢您为解决此问题而提供的任何帮助。

更新 3:仍在寻找理想的解决方案

我仍然希望有一个解决方案。尽管我已将解决方案标记为已接受,但建议的解决方案并不理想。因为有不同级别的集成测试(从端到端测试到具有一小组依赖项的非常狭窄的测试范围)。

【问题讨论】:

  • 注入 spring-jpa bean 后,监视该对象而不是模拟。

标签: spring-boot mockito spring-data-jpa


【解决方案1】:

这里有几个问题。我假设您正在尝试验证 PersonRepository 是否被嘲笑。但是,在您的测试中,您写道:

assertThat(mockingDetails(personService).isMock(), is(true));

你不是在嘲笑PersonService,所以它会失败这个断言是有道理的。


另一个问题是 Spring Data JPA 还将创建一个名为 personRepository 的 bean。因此,除非您更改其名称,否则您的模拟存储库 bean 将被忽略:

@Bean
@Profile("mock-person-repository")
// Change method name to change bean name
public PersonRepository personRepositoryMock() {
    logger.info("Mocking: {}", PersonRepository.class);
    return mock(PersonRepository.class);
}

但是如果你这样做了,那么就会有两个PersonRepository 类型的bean,所以将它自动装配到你的服务中将会失败。要解决这个问题,您必须阻止它使用 Spring Data JPA 创建存储库。


无论如何,一个更清洁的解决方案是使用 MockitoJUnitRunner 代替 Springs 测试运行器。您所要做的就是使用@InjectMocks 注释您要测试的类以及您要模拟和注入的所有依赖项@Mock:像这样:

@InjectMocks
private PersonService personService;
@Mock
private SomeBean someBean;
@Mock
private PersonRepository repository;

然后您可以删除TestConfig 并删除测试中的所有注释并用@RunWith(MockitoJUnitRunner.class) 替换它们。

【讨论】:

  • 你是绝对正确的。我犯了一些错误,应该检查PersonRepository 而不是PersonService。我会更新问题以反映这一点。
  • 切换到 @RunWith(MockitoJUnitRunner.class) 可能不是实际实现的理想解决方案,因为 (1) 必须显式模拟服务的所有依赖项,以及 (2) @InjectMocks 如果无法模拟,则会静默失败任何依赖项。
  • (1) 没错,但这允许您将范围缩小到单个类,而不是一次测试多个类。 (2) 如果这是一个问题,那么您可以使用构造函数自动装配并在测试中手动创建服务实例,如下所述:tedvinke.wordpress.com/2014/02/13/…
猜你喜欢
  • 2022-12-11
  • 2016-01-08
  • 1970-01-01
  • 2017-04-30
  • 1970-01-01
  • 2013-08-23
  • 2017-08-20
  • 2018-08-06
  • 2018-06-15
相关资源
最近更新 更多