【发布时间】:2012-10-03 04:55:11
【问题描述】:
我有一个drools 规则文件,它在规则中使用服务类。所以一个规则做这样的事情:
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 方法,知道该怎么做吗?
问候,
迈克尔
【问题讨论】:
-
您能否更准确地说明您如何知道 mockito 失败的原因?此外,虽然与问题无关,但您应该注意不建议真正推荐模拟值,您应该自己创建一个值实例,可能在您的测试中使用自定义工厂或私有构造函数等...
-
你能不能多展示一些
BaseTestDomainIntegration和spring 配置,如果有关系的话。 -
嗨,brice,我添加了更多信息。看到子弹
-
basetestdomainintegration 是我设置 spring 测试上下文的地方。它带有注释 @TransactionConfiguration(defaultRollback = true) @ContextConfiguration(locations = { "classpath:domain-TestContext.xml" })
-
在您的上下文中,您有
CountryServicebean 吗? 如果是这样,您可能想在 spring 上下文中创建模拟。我相信组合 Spring/some aspect/testng 基类可能会在创建模拟后触发一些额外的行为,在你的情况下,我怀疑是 mockito mock 的替代品。
标签: spring testng mockito transactional