我有一个drools规则文件,该文件在规则中使用服务类。因此,一条规则就是这样的:

eval(countryService.getCountryById(1)!= null)

在以@service和@Transactional(propagation = Propagation.SUPPORTS)注释的验证服务中,在无状态知识库中使用了drools文件,并添加了应该在drool中使用的事实。完成此操作后,将调用session.execute(facts)并启动规则引擎。

为了测试规则,我想对countryService.getCountryById()进行 stub 处理。使用Mockito没什么大问题。对于其他使用流口水设置的服务也做了此操作,并且效果很好。但是,在这种特殊情况下,countryService没有 stub ,我也不知道为什么。花了很多时间并检查了我的代码后,我发现在服务上方使用@Transactional或缺少此批注会有所不同。缺少@Transaction使得Mockito可以毫无问题地模拟countryservice,将@transactional放置在适当的位置会导致Mockito失败(没有任何错误或提示),因此无法使用原来的countryservice对象进行注入(inject)。

我的问题是为什么此注释会导致此问题。设置@Transactional时,为什么不能 mock 模拟?我注意到, mock 失败了,因为当我调试并检查countryService并将其作为全局变量添加到drools session 中时,我在debugwindow中检查countryservice时看到以下区别:

带有@transactional的

  • :countryService的值为CountryService $$ EnhancerByCGLIB $$ b80dbb7b
  • 不含@transactional:countryService的
  • 的值为CountryService $$ EnhancerByMockitoWithCGLIB $$ 27f34dc1

  • 另外,使用@transactional时,会在countryservice方法中找到我的断点getCountryById,并且调试器将在该断点处停止,但是如果没有@transactional,我的断点将被跳过,因为 mock 会绕过它。

    验证服务:
    @Service
    @Transactional(propagation=Propagation.SUPPORTS)
    public class ValidationService
    {
      @Autowired
      private CountryService countryService;
    
      public void validateFields(Collection<Object> facts)
      {
        KnowledgeBase knowledgeBase = (KnowledgeBase)AppContext.getApplicationContext().getBean(knowledgeBaseName);
        StatelessKnowledgeSession session = knowledgeBase.newStatelessKnowledgeSession();
        session.setGlobal("countryService", countryService);
        session.execute(facts);
    
      }
    

    和测试类:
    public class TestForeignAddressPostalCode extends BaseTestDomainIntegration
    {
    
      private final Collection<Object> postalCodeMinLength0 = new ArrayList<Object>();
    
      @Mock
      protected CountryService countryService;
    
      @InjectMocks
      private ValidationService level2ValidationService;
    
    
      @BeforeMethod(alwaysRun=true)
      protected void setup()
      {
        // Get the object under test (here the determination engine)
        level2ValidationService = (ValidationService) getAppContext().getBean("validationService");
        // and replace the services as documented above.
        MockitoAnnotations.initMocks(this);
    
        ForeignAddress foreignAddress = new ForeignAddress();
        foreignAddress.setCountryCode("7029");
        foreignAddress.setForeignPostalCode("foreign");
    
        // mock country to be able to return a fixed id
        Country country = mock(Country.class);
        foreignAddress.setLand(country);
        doReturn(Integer.valueOf(1)).when(country).getId();
    
        doReturn(country).when(countryService).getCountryById(anyInt());
    
        ContextualAddressBean context = new ContextualAddressBean(foreignAddress, "", AddressContext.CORRESPONDENCE_ADDRESS);
        postalCodeMinLength0.add(context);
      }
    
      @Test
      public void PostalCodeMinLength0_ExpectError()
      {
        // Execute
        level2ValidationService.validateFields(postalCodeMinLength0, null);
    
      }
    

    如果我想保留此@transactional批注但又能够对countryservice方法进行 stub ,该怎么办?

    问候,

    麦可

    最佳答案

    请注意,从Spring 4.3.1开始,ReflectionTestUtils应该自动打开代理。所以

    ReflectionTestUtils.setField(validationService, "countryService", countryService);
    

    即使您的countryService@Transactional@Cacheable ...注释(即,在运行时隐藏在代理后面),现在也应该可以使用

    相关问题:SPR-14050

    关于spring - 事务注释避免了服务被 mock ,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/12857981/

    10-10 09:49