假设我有这个:
@Transactional(rollbackFor = NotificationException.class)
public interface PersonManagerService {
public void addPerson(Person person);
}
和实施:
public class PersonManagerServiceImpl implements PersonManagerService {
public OtherService otherService;
public void addPerson(Person person) {
// stuff
}
// getter and setter for otherService
}
如果仍然使用addPerson方法访问数据库,我将如何模拟otherService依赖项?
我的方案是我想测试一个特定的异常导致回滚正在添加的人的保存。这个异常来自OtherService类,我不想调用它的真实版本。我目前正在使用Spring Transaction注释,所以我有这样的测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {various locations})
public class Test() {
@Autowired
PersonManagerService service;
@Test
public void test() {
// how to call setOtherService so it can be a mock?
}
如果我尝试转换自动连线bean,我会得到一个IllegalArgumentException,因为它是一个代理。如果我让Impl不使用接口,我可以使用CGLIB,但我不想这样做。如果我以编程方式创建impl,那么它就不会绑定到事务流中。我还有其他选择吗?
答案 0 :(得分:2)
有多种方法可以解决这个问题(从最佳到最差):
利用@Profile
s - 在Spring 3.1中,您可以将profile名称与每个bean相关联。当您启动应用程序上下文时,您将提供活动配置文件,并且只会实例化没有任何关联的配置文件或提供的配置文件匹配的bean。这是一个非常强大的机制。
@Profile("prd")
public class PersonManagerServiceImpl implements PersonManagerService
//...
@Profile("test")
public class PersonManagerServiceMock implements PersonManagerService
//...
@ContextConfiguration
@ActiveProfiles(value = "test")
public class Test {
使用primary
或@Primary
- 如果您在otherService
中自动装配PersonManagerServiceImpl
,则可以使用{定义第二个模拟bean {1}}属性或primary="true"
注释。当自动装配时,Spring会更喜欢初级豆。
解包交易代理(请参阅:Is it possible to unproxy a Spring bean?和Mocking a property of a CGLIB proxied service not working)以访问setter。有点hacky,但适合其他人