How to verify that transaction rollback works while mocking dependencies?

Suppose I have this:

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

and implementation:

 public class PersonManagerServiceImpl implements PersonManagerService { public OtherService otherService; public void addPerson(Person person) { // stuff } // getter and setter for otherService } 

How could I make fun of the otherService dependency when using the addPerson method in the database?

My scenario is that I want to verify that a specific exception causes the save of the added face to be rolled back. This exception comes from the OtherService class, which I do not want to call the real version. 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 use an auto wired bean, I get an IllegalArgumentException because it is a proxy. If I made Impl not use the interface, I could use CGLIB, but I do not want to do this. If I create the program code, then it is not tied to the transaction flow. What other options do I have?

+4
source share
1 answer

There are various ways to solve this program (from best to worst):

  • use @Profile s - in Spring 3.1, you can associate profile name with each bean. When you launch the application context, you provide active profiles, and only beans will be instantiated without any associated profile or with the provided profile. 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 loading otherService in PersonManagerServiceImpl , you can define a second bean layout with the primary="true" attribute or the @Primary annotation. Spring prefers primary beans for auto-preparation.

  • deploy a transactional proxy (see: Is it possible to use unproxy a Spring bean? and Mock the CGLIB proxied service property does not work ) to access setter. A bit hacked, but works for others.

+2
source

Source: https://habr.com/ru/post/1393767/


All Articles