问题描述
我有一个Drools规则文件,该文件在规则中使用服务类.因此,一条规则就是这样:
I have a drools rule file which uses service classes in the rules. So one rule does something like this:
eval(countryService.getCountryById(1)!= null)
eval(countryService.getCountryById(1) != null)
在以@service和@Transactional(propagation = Propagation.SUPPORTS)注释的验证服务中,在无状态知识库中使用了drools文件,并添加了应该在drool中使用的事实.完成后,将调用session.execute(facts)并启动规则引擎.
In a validationservice that is annotated with @service and @Transactional(propagation=Propagation.SUPPORTS) the drools file is used in a statelessKnowledgebase and facts are added that should be used in the drool. Once that is done the session.execute(facts) is invoked and the rule engine starts.
为了测试规则,我想对countryService.getCountryById()进行存根处理.使用Mockito没什么大问题.对于其他使用流口水设置的服务也做了此操作,并且效果很好.但是,在这种特殊情况下,countryService没有存根,我也不知道为什么.花了很多时间并检查了我的代码后,我发现在服务上方使用@Transactional或缺少此批注会有所不同.缺少@Transaction使得Mockito可以毫无问题地模拟countryservice,将@transactional放置在适当的位置会导致Mockito失败(没有任何错误或提示),因此无法使用原来的countryservice对象进行注入.
In order to test the rules I would like to stub the countryService.getCountryById(). No big problem using mockito. Done this for other service that use a drools setup as well and it worked fine. However in this particular case the countryService was not stubbed and I couldn't figure out why. After spending a lot of time and checking my code I found that having @Transactional above the service or lacking this annotation made the difference. Lacking the @Transaction made mockito mock the countryservice without any problem, having the @transactional in place caused mockito to fail (without any error or hint) injecting the mock so the original countryservice object was used.
我的问题是,为什么此注释会导致此问题.设置@Transactional时,为什么不能嘲笑模拟?我注意到,嘲笑失败了,因为当我调试并检查countryService并将其作为全局变量添加到drools会话时,我在调试窗口中检查countryservice时看到以下区别:
My question is why this annotation causes this problem. Why can't mockito inject the mocks when @Transactional is set? I've noticed that mockito is failing as when I debug and inspect the countryService when it is being added as global to the drools session I see the following difference when I inspect the countryservice in my debugwindow:
-
具有@transactional:countryService的值为CountryService $$ EnhancerByCGLIB $$ b80dbb7b
with @transactional: countryService has the value CountryService$$EnhancerByCGLIB$$b80dbb7b
不带@transactional:countryService的值为CountryService $$ EnhancerByMockitoWithCGLIB $$ 27f34dc1
without @transactional:countryService has the value CountryService$$EnhancerByMockitoWithCGLIB$$27f34dc1
除了@transactional之外,还可以在countryservice方法中找到我的断点getCountryById,并且调试器在该断点处停止,但是没有@transactional时,我的断点将被跳过,因为mockito绕过了它.
In addition with @transactional my breakpoint in the countryservice methode getCountryById is found and the debugger stops at that breakpoint, but without the @transactional my breakpoint is skipped as mockito bypasses it.
ValidationService:
ValidationService:
@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方法,该怎么办?
Any idea what to do if I want to keep this @transactional annotation but also be able to stub the countryservice methodes?
致谢
迈克尔
推荐答案
请注意,从Spring 4.3.1开始, ReflectionTestUtils 应该会自动打开代理.所以
Please note that since Spring 4.3.1, ReflectionTestUtils should automatically unwrap proxies. So
ReflectionTestUtils.setField(validationService, "countryService", countryService);
即使您的countryService
用@Transactional
,@Cacheable
...(即在运行时隐藏在代理后面)注释,
现在也应该可以工作
should now work even if your countryService
is annotated with @Transactional
, @Cacheable
... (that is, hidden behind a proxy at runtime)
相关问题: SPR-14050
这篇关于事务注释避免了服务被嘲笑的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!