0

How to verify internal proxy calls in unit testing by using Mockito framework?

I am trying to write a test for doAllTasks() method and verify that doSingleTask() was called a certain amount of times. Obviously I can not split my service into two because these methods have the same meaning. The simplest solution is to add setProxy() method but disadvantage of this is that I will need to add test related code to my service definition. Any ideas?

@Service
public class XXXServiceImpl implements XXXService, BeanNameAware {

  private String name;

  private XXXService proxy;

  @Autowired
  private ApplicationContext applicationContext;

  @Override
  public void setBeanName(String name) {
    this.name = name;
  }

  @PostConstruct
  public void postConstruct() {
    proxy = (XXXService)applicationContext.getBean(name);
  }

  @Transactional
  public void doAllTasks(){
    for(...)
      proxy.doSingleTask(...);
  }

  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public void doSingleTask(...){
  }
}
Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
Marek Raki
  • 3,056
  • 3
  • 27
  • 50

2 Answers2

2

A simple way to fix this is to add a setter for the proxy and overwrite the field in the unit test with a simple mock. If you also add a getter, you can restore it after the test.

The main drawback is that this prevents you from running the tests in parallel unless you create a new ApplicationContext for this test.

An alternative might be to give the field a @Qualifier so you can define two different beans in your test's configuration. In production, you simply return the singleton.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • Is it a good practice to add setProxy() and getProxy() to my service interface? Is not like mixing test related code with service related ? Normally these methods will be useless. Could you explain more this alternative with @Qualifier? – Marek Raki Sep 05 '14 at 09:14
  • 1
    No. Just add it the implementation and cast in the unit test. After all, we're just adding this for the sake of the test. You can also make those methods `protected`; the unit test can then see them since it's in the same package. – Aaron Digulla Sep 05 '14 at 09:22
  • 1
    About the `@Qualifier`: You can get the bean with `proxy = (XXXService)applicationContext.getBean("XXXServiceProxy");` instead of using the same name as the singleton. That would allow you to define two beans in the unit test config. In the prod config, use an alias so the two beans become the same. – Aaron Digulla Sep 05 '14 at 09:24
  • Actually there is one problem more because I can not cast to implementation. Is not mentioned in this post but my service is @Validated so it creates a proxy :) While casting I get java.lang.ClassCastException: com.sun.proxy.$Proxy22 cannot be cast to xxx.xxx.services.XXXServiceImpl I have validation turned on for additional checking of correctness of results. Now probably I have to disable it in configuration and do manually with resulting objects. – Marek Raki Sep 05 '14 at 10:33
  • 1
    See http://stackoverflow.com/questions/8121551/is-it-possible-to-unproxy-a-spring-bean for unwrapping proxies. – Aaron Digulla Sep 05 '14 at 11:33
0
  1. Inject mock of ApplicationContext.
  2. Mock getBean meatod to return mock of XXXService
  3. Verify doSingleTask invoked
talex
  • 17,973
  • 3
  • 29
  • 66
  • This may be too complicated. If I mock ApplicationContext then I will have two getBean(XXXService.class) calls. The first one when I initialize the bean and the second from @PostConstruct method. I can do Mockito.when() with multiple returns but this sounds for me like a hardcode. Probably I have to have also a real instance of ApplicationContext to redirect getBean() calls for the rest of autowired fields. Am I right ? – Marek Raki Sep 05 '14 at 09:06