Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mockito fails to verify the second time on the proxied bean #6871

Closed
igormukhin opened this issue Sep 12, 2016 · 10 comments
Closed

Mockito fails to verify the second time on the proxied bean #6871

igormukhin opened this issue Sep 12, 2016 · 10 comments
Assignees
Labels
type: bug A general bug
Milestone

Comments

@igormukhin
Copy link

igormukhin commented Sep 12, 2016

Version: Spring Boot 1.4.0-RELEASE

It looks like there is an issue with verifing multiple times with Mockito on a proxied bean.

Test-case:

public class SpringBootMockitoTest {

    private AnnotationConfigApplicationContext context;

    private SomeServiceWithTransact someServiceWithTransact;
    private SomeServiceNoTransact someServiceNoTransact;

    @Configuration
    @EnableTransactionManagement
    public static class Config {

        @Bean
        public SomeServiceWithTransact someServiceWithTransact() {
            return new SomeServiceWithTransact();
        }

        @Bean
        public SomeServiceNoTransact someServiceNoTransact() {
            return new SomeServiceNoTransact();
        }

        @Bean
        public PlatformTransactionManager tm() {
            return new DataSourceTransactionManager(dataSource());
        }

        @Bean
        public DataSource dataSource() {
            return DataSourceBuilder.create()
                             .driverClassName("org.h2.Driver")
                             .url("jdbc:h2:mem:tst;DB_CLOSE_DELAY=-1")
                             .username("sa")
                             .password("")
                             .build();
        }

    }

    private static class SomeServiceNoTransact {

        public void normalMethod(int param1) {
            // do nothing
        }

    }

    private static class SomeServiceWithTransact {

        @Transactional
        public void transactionalMethod(int param1) {
            // do nothing
        }

    }

    @Before
    public void setUp() throws Exception {
        context = new AnnotationConfigApplicationContext(Config.class);
        context.start();

        someServiceWithTransact = context.getBean(SomeServiceWithTransact.class);
        someServiceNoTransact = context.getBean(SomeServiceNoTransact.class);
    }

    @After
    public void tearDown() throws Exception {
        context.stop();
    }

    @Test
    public void testNormalMethod() throws Exception {
        SomeServiceNoTransact serviceSpy = spy(someServiceNoTransact);

        // when
        serviceSpy.normalMethod(1);

        // then
        Mockito.verify(serviceSpy, Mockito.times(1)).normalMethod(1);
        Mockito.verify(serviceSpy, Mockito.times(1)).normalMethod(anyInt());
    }

    @Test
    public void testTransactionalMethod() throws Exception {
        SomeServiceWithTransact serviceSpy = spy(someServiceWithTransact);

        // when
        serviceSpy.transactionalMethod(1);

        // then
        Mockito.verify(serviceSpy, Mockito.times(1)).transactionalMethod(1);
        Mockito.verify(serviceSpy, Mockito.times(1)).transactionalMethod(anyInt());
    }
}

Now:

  • testNormalMethod() will be green and all right
  • testTransactionalMethod() will be red and it is not all right. The only difference to the first test in the code is that the method unter test is annotated with @Transactional

testTransactionalMethod() fails with:

org.mockito.exceptions.misusing.UnfinishedVerificationException: 
Missing method call for verify(mock) here:
-> at de.audi.pbt.problem.service.MockitoIT.testTransactionalMethod(MockitoIT.java:123)

But!

  • If you remove @transactional form the bean unter test, the test will become green.
  • If you remove the first call to verify, the test will become green.
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Sep 12, 2016
@snicoll
Copy link
Member

snicoll commented Sep 12, 2016

@igormukhin there's nothing specific to Spring Boot in your project. I have the feeling that it could be a mockito usage problem. Please create an issue in the Spring Framework issue tracker.

Also, rather than pasting the full code in the description, please create a project that one can run, it's much more convenient.

@snicoll snicoll closed this as completed Sep 12, 2016
@snicoll snicoll added status: invalid An issue that we don't feel is valid and removed status: waiting-for-triage An issue we've not yet triaged labels Sep 12, 2016
@philwebb
Copy link
Member

@igormukhin We faced a very similar issue with the new @MockBean and @SpyBean annotations (see #5837). Have you tried using them instead of calling the spy method directly (you'll need to try with Spring Boot 1.4.1.BUILD-SNAPSHOT)

@igormukhin
Copy link
Author

igormukhin commented Sep 12, 2016

@snicoll @philwebb I discovered this issue by using @SpyBean in an integration test for our Spring Boot web application.

@wilkinsona
Copy link
Member

@igormukhin In that case the code above isn't much help as it doesn't use @SpyBean. Can you please provide a complete, minimal sample that illustrates the problem you're having and uses @SpyBean?

@philwebb
Copy link
Member

@igormukhin Also, please ensure you're using 1.4.1.BUILD-SNAPSHOT since there are know issues with 1.4.0.RELEASE.

@igormukhin
Copy link
Author

igormukhin commented Sep 12, 2016

@wilkinsona @philwebb Here is the project you requested with @SpyBean - https://github.com/igormukhin/spring-boot-issue-6871
Same results for the snapshot.

@philwebb philwebb added status: waiting-for-triage An issue we've not yet triaged type: bug A general bug and removed status: invalid An issue that we don't feel is valid labels Sep 12, 2016
@philwebb philwebb self-assigned this Sep 12, 2016
@philwebb philwebb added this to the 1.4.1 milestone Sep 12, 2016
@philwebb philwebb removed the status: waiting-for-triage An issue we've not yet triaged label Sep 12, 2016
@philwebb
Copy link
Member

Thanks! I think we need to refine our MockitoAopProxyTargetInterceptor

@philwebb philwebb reopened this Sep 12, 2016
@sbrannen
Copy link
Member

FYI: the solution to the original code example is simply to invoke AopTestUtils.getUltimateTargetObject(), passing in the someServiceWithTransact that was retrieved from the ApplicationContext.

For example, the following works fine:

    @Test
    public void testTransactionalMethod() throws Exception {
        SomeServiceWithTransact ultimateTargetObject = AopTestUtils.<SomeServiceWithTransact> getUltimateTargetObject(
            someServiceWithTransact);

        SomeServiceWithTransact serviceSpy = spy(ultimateTargetObject);

        // when
        serviceSpy.transactionalMethod(1);

        // then
        verify(serviceSpy, times(1)).transactionalMethod(1);
        verify(serviceSpy, times(1)).transactionalMethod(anyInt());
    }

Regards,

Sam

@kriegaex
Copy link

kriegaex commented Jul 9, 2020

Thanks @sbrannen for the workaround, I just used it in order to answer StackOverflow question #62698827. (BTW, I never used Spring, I am just interested in AOP topics and stumbled upon the question.) There you can also find a link to yet another GitHub sample project.

I am mentioning this in order to remind any possibly involved maintainers that this is still an open issue which ought to be addressed eventually.

@wilkinsona
Copy link
Member

Thanks, @kriegaex. While you can use the same technique to avoid the problem described on Stack Overflow, it is a different problem to the one fixed in this issue. I'm not sure if we'll be able to automatically avoid the advice being executed when setting up expectations on the spy but we can certainly take a look. I've opened #22281.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

No branches or pull requests

7 participants