4

Suppose I have this:

@Transactional(rollbackFor = NotificationException.class)
public interface PersonManagerService {
   public void addPerson(Person person);
}

and an implementation:

public class PersonManagerServiceImpl implements PersonManagerService {

public OtherService otherService;

public void addPerson(Person person) {
// stuff
}

// getter and setter for otherService
}

How would I go about mocking the otherService dependency while still having the addPerson method hit the database?

My scenario is that I want to test that a particular exception causes a rollback of the saving of the person that is being added. This exception would come from the OtherService class, which I don't want to call the real version of. I am currently using Spring Transaction annotations so I have a test like this:

@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?
}

If I try to cast the auto wired bean, I get an IllegalArgumentException because it is a proxy. If I made the Impl not use an interface, I could use CGLIB but I don't want to do that. If I create the impl programmatically, then it's not tied into the transaction flow. What other options do I have?

AHungerArtist
  • 9,332
  • 17
  • 73
  • 109

1 Answers1

2

There are various ways you can tackle this program (from best to worst):

  • take advantage of @Profiles - in Spring 3.1 you can associate profile name with every bean. When you are starting an application context you provide active profiles and only beans without any profile associated or with profile matching provided will be instantiated. This is a very powerful mechanism.

    @Profile("prd")
    public class PersonManagerServiceImpl implements PersonManagerService
    
    //...
    
    @Profile("test")
    public class PersonManagerServiceMock implements PersonManagerService
    
    //...
    
    @ContextConfiguration
    @ActiveProfiles(value = "test")
    public class Test {
    
  • use primary or @Primary - if you are autowiring otherService in PersonManagerServiceImpl you can define a second mock bean with primary="true" attribute or @Primary annotation. Spring will prefer primary beans when autowiring.

  • unwrap transactional proxy (see: Is it possible to unproxy a Spring bean? and Mocking a property of a CGLIB proxied service not working) to access setter. A bit hacky, but works for others

Community
  • 1
  • 1
Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • This was great information, thanks! I was aware of AopUtils but couldn't figure out how to use it to get back to the impl, but your answer in the link showed me how. We'll be moving to 3.1 soon and plan to rework some of our config, so that will hopefully be a good time to get in on that. – AHungerArtist Jan 30 '12 at 14:28