【问题标题】:How to inject mock into @Service that has @Transactional如何将模拟注入具有@Transactional 的@Service
【发布时间】:2014-02-03 03:34:30
【问题描述】:

我的单元测试中有任何问题,我有类似的东西。如果blargh 函数使用 Transactional 注释,则模拟注入会在 someService 上被覆盖。如果我删除 Transactional ,则模拟会留在那里。从代码中可以看出,当服务中的函数被注释为事务时,Spring 会延迟加载服务,但在没有注释时会急切地加载服务。这会覆盖我注入的模拟。

有没有更好的方法来做到这一点?

@Component
public class SomeTests
{
  @Autowired
  private SomeService someService;

  @Test
  @Transactional
  public void test(){
    FooBar fooBarMock = mock(FooBar.class);
    ReflectionTestUtils.setField(someService, "fooBar", fooBarMock);
  }
}

@Service
public class someService
{
  @Autowired FooBar foobar;

  @Transactional // <-- this causes the mocked item to be overridden
  public void blargh()
  {
    fooBar.doStuff();
  }
}

【问题讨论】:

  • 你能生成代码(和配置)来重现这个吗?
  • 根据您的问题,我假设您没有单元测试,而是使用 Spring 上下文和一些模拟的 bean 进行集成测试。你如何将 FooBar 类的模拟注入到 Spring 上下文中?您使用 Springockito 还是尝试手动操作?提供测试课程,以增加您获得准确帮助的机会。
  • 你最后是怎么解决这个问题的?
  • 我最终重构了我的代码以分离出概念,以便在这种情况下更容易测试。

标签: java spring unit-testing mockito


【解决方案1】:

或许您可以尝试通过以下方式实现您的测试:

@Component
@RunWith(MockitoJUnitRunner.class)
public class SomeTests
{
  @Mock private FooBar foobar;
  @InjectMocks private final SomeService someService = new SomeService();


  @Test
  @Transactional
  public void test(){
    when(fooBar.doStuff()).then....;
    someService.blargh() .....
  }
}

由于没有您的配置和相关代码,我现在无法尝试。但这是测试服务逻辑的常用方法之一。

【讨论】:

  • 当然它需要 Mockito,但它可以让你的测试保持简单和干净。
  • 这会起作用,但是对于我的测试,有时我需要注入模拟,而其他时候我不需要。这取决于测试正在测试什么。更糟糕的情况是我可以这样做,只需将需要它的测试分开到他们自己的类中,但我希望将这些测试组合在一起。
  • 好的,我现在和你在一起!可能我会单独保留这些测试,并且会有一套回归测试和一套这样的集成测试。不知道注入真实对象而不是模拟的用例是什么。我试图始终专注于测试我为其创建 jUnit 的 bean,而其他一切都被嘲笑。 (并在另一层测试)。但正如我所说,我不知道你的用例。
  • 今天尝试了这个,使用@InjectMocks,但它似乎有同样的问题,当它延迟加载其余服务时,模拟被覆盖了。
  • 使用@Transactional 本身不会导致bean 的延迟加载,因此它们是在bean 创建后注入的。唯一的区别可能是如果没有 CGLIb 用于事务处理,事务 bean 将被代理。我会用一个spring bean和一个测试创建一个非常简单的项目,看看你是否有同样的问题。如果它可以工作并且正如你所说的你想要一个模拟版本和一个非模拟版本,你可能会持有两个测试中服务的引用。 MockedService 和 nonMockedService。但是没有聪明的主意
【解决方案2】:

使用 Spring @Profile 功能 - bean 可以关联到某个组,并且可以通过注释激活或停用该组。

查看blog post 和documentation 以获得更详细的说明,这是如何定义生产服务和两组模拟服务的示例:

@Configuration
@Profile("production")
public static class ProductionConfig {
    @Bean
    public InvoiceService realInvoiceService() {
        ...
    }
    ...
}

@Configuration
@Profile("testServices")
public static class TestConfiguration {
    @Bean
    public InvoiceService mockedInvoiceService() {
        ...
    }
    ...
}

@Configuration
@Profile("otherTestServices")
public static class OtherTestConfiguration {
    @Bean
    public InvoiceService otherMockedInvoiceService() {
        ...
    }
    ...
}

这是在测试中使用它们的方法:

@ActiveProfiles("testServices")
public class MyTest extends SpringContextTestCase {
    @Autowired
    private MyService mockedService;

    // ...
}

@ActiveProfiles("otherTestServices")
public class MyOtherTest extends SpringContextTestCase {
    @Autowired
    private MyService myOtherMockedService;

    // ...
}

【讨论】:

  • 请解释一下这会做什么。
  • 在这种情况下,我不是在模拟 SomeService,而是在模拟 SomeService 使用的内部服务。
  • 我已经更新了另一种方法,以确保只有测试 bean 被连接,使用显式 XML 配置进行测试而不扫描服务包总是可以肯定的,虽然不太方便
【解决方案3】:

我有完全相同的问题,我通过使用Mockito.any() 作为参数来解决它

例如:

when(transactionalService.validateProduct(id)).thenReturn("")
=> when(transactionalService.validateProduct(Mockito.any())).thenReturn("")

【讨论】:

    猜你喜欢
    • 2012-12-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-05
    • 1970-01-01
    相关资源
    最近更新 更多