我有一个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的
另外,使用@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/