如何在模拟依赖项时测试事务回滚是否有效?

时间:2012-01-30 14:09:03

标签: java hibernate spring testing transactions

假设我有这个:

@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,那么它就不会绑定到事务流中。我还有其他选择吗?

1 个答案:

答案 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,但适合其他人