我有一个流口水规则文件,它在规则中使用服务类。所以一个规则做这样的事情:
eval(countryService.getCountryById(1) != null)
在使用 @service 和 @Transactional(propagation=Propagation.SUPPORTS) 注释的验证服务中,drools 文件用于无状态知识库中,并添加了应在 drool 中使用的事实。一旦完成,将调用 session.execute(facts) 并启动规则引擎。
为了测试规则,我想存根 countryService.getCountryById()。使用 mockito 没有什么大问题。为其他使用 drools 设置的服务完成此操作,并且效果很好。但是在这种特殊情况下, countryService 没有被存根,我不知道为什么。在花了很多时间检查我的代码之后,我发现在服务上方使用 @Transactional 或缺少此注释会有所不同。缺少@Transaction 使得mockito 可以毫无问题地模拟countryservice,而使用@transactional 会导致mockito 失败(没有任何错误或提示)注入模拟,因此使用了原始的countryservice 对象。
我的问题是为什么这个注释会导致这个问题。为什么在设置@Transactional 时不能模拟注入模拟?我注意到,当我调试和检查 countryService 时,mockito 失败了,当它作为全局添加到 drools 会话时,当我在调试窗口中检查 countryservice 时,我看到以下差异:
使用@transactional:countryService 的值为 CountryService$$EnhancerByCGLIB$$b80dbb7b
没有 @transactional:countryService 的值为 CountryService$$EnhancerByMockitoWithCGLIB$$27f34dc1
除了@transactional,我在countryservice方法getCountryById中找到了断点,调试器在该断点处停止,但没有@transactional,我的断点被跳过,因为mockito绕过它。
验证服务:
@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 方法,知道该怎么做吗?
问候,
迈克尔