我有一个drools规则文件,该文件在规则中使用服务类。因此,一条规则就是这样的:
eval(countryService.getCountryById(1)!= null)
在以@service和@Transactional(propagation = Propagation.SUPPORTS)注释的验证服务中,在无状态知识库中使用了drools文件,并添加了应该在drool中使用的事实。完成后,将调用session.execute(facts)并启动规则引擎。
为了测试规则,我想对countryService.getCountryById()进行存根。使用Mockito没什么大问题。对于其他使用流口水设置的服务也做了此操作,并且效果很好。但是在这种特殊情况下,countryService没有被打断,我也不知道为什么。花了很多时间并检查了我的代码后,我发现在服务上方使用@Transactional或缺少此批注会有所不同。缺少@Transaction使得Mockito可以毫无问题地模拟countryservice,将@transactional放置在适当的位置会导致Mockito失败(没有任何错误或提示),因此无法注入原始模拟的countryservice对象。
我的问题是为什么此注释会导致此问题。设置@Transactional时,为什么不能嘲笑模拟?我已经注意到,嘲笑失败了,因为当我调试并检查countryService并将其作为全局变量添加到drools会话时,我在调试窗口中检查countryservice时看到以下区别:
使用@transactional:countryService的值为CountryService $$ EnhancerByCGLIB $$ b80dbb7b
没有@transactional:countryService的值是CountryService $$ EnhancerByMockitoWithCGLIB $$ 27f34dc1
另外,使用@transactional时,会在countryservice方法中找到我的断点getCountryById,调试器将在该断点处停止,但是没有@transactional时,我的断点将被跳过,因为嘲笑会绕过它。
验证服务:
@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方法进行存根,该怎么办?
问候,
发生的事情是你的ValidationService被包装在JdkDynamicAopProxy中,因此,当Mockito将模拟程序注入到服务中时,它看不到任何将其注入的字段。你需要执行以下两项操作之一:
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
ValidationService validationService = (ValidationService) unwrapProxy(level2ValidationService);
ReflectionTestUtils.setField(validationService, "countryService", countryService);
}
public static final Object unwrapProxy(Object bean) throws Exception {
/*
* If the given object is a proxy, set the return value as the object
* being proxied, otherwise return the given object.
*/
if (AopUtils.isAopProxy(bean) && bean instanceof Advised) {
Advised advised = (Advised) bean;
bean = advised.getTargetSource().getTarget();
}
return bean;
}