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 [SPR-14701] #19265

Closed
spring-projects-issues opened this issue Sep 12, 2016 · 7 comments
Assignees
Labels
in: test Issues in the test module status: invalid An issue that we don't feel is valid

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Sep 12, 2016

Igor Mukhin opened SPR-14701 and commented

I discovered this bug by using @SpyBean in a Spring Boot 1.4.0 web application. But the bug is reproducable without Spring Boot and @SpyBean, so snicoll said I should file the bug here.


It looks like there is an issue with verifying 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 under test, the test will become green.
  • If you remove the first call to verify, the test will become green.

Affects: 4.3.2

@spring-projects-issues
Copy link
Collaborator Author

Igor Mukhin commented

The project that shows the issue: https://github.com/igormukhin/spring-boot-issue-6871

@spring-projects-issues
Copy link
Collaborator Author

Stéphane Nicoll commented

In the meantime, it turns out you were using Spring Boot and that @SpyBean needed to take that use case into account. This is now the case and your project works fine with 1.4.1.BUILD-SNAPSHOT.

Looking at the original project you've reported, you're spying on the bean directly (i.e. the proxy) so if you want to pursue that way (i.e. not using @SpyBean) you'll have to replicate what we do there in your own code. But maybe Sam Brannen feels differently and some helper can be added to spring-test for this?

@spring-projects-issues
Copy link
Collaborator Author

Sam Brannen commented

I have admittedly not looked at the sample project in detail, but....

Have you considered using AopTestUtils.getUltimateTargetObject(Object) from spring-test?

@spring-projects-issues
Copy link
Collaborator Author

Igor Mukhin commented

You mean like SomeServiceWithTransact serviceSpy = spy(AopTestUtils.getUltimateTargetObject(someServiceWithTransact));?

@spring-projects-issues
Copy link
Collaborator Author

Sam Brannen commented

Yes, that's exactly what I meant.

I just tested it locally, and the following works fine:

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

		SomeServiceWithTransact serviceSpy = spy(ultimateTargetObject);

		// when
		serviceSpy.transactionalMethod(1);

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

I am therefore closing this issue as Invalid.

Regards,

Sam

@spring-projects-issues
Copy link
Collaborator Author

Igor Mukhin commented

Thanks, ))) I was testing it right now the same time with you.

igormukhin/spring-boot-issue-6871@448cb1e

Both getTargetObject(...) and getUltimateTargetObject(...) solve the issue.

@spring-projects-issues
Copy link
Collaborator Author

Sam Brannen commented

Great!

I'm glad it works for you.

By the way, although getTargetObject may work for you, I would typically recommend using getUltimateTargetObject just to be on the safe side. ;)

@spring-projects-issues spring-projects-issues added type: bug A general bug status: invalid An issue that we don't feel is valid in: test Issues in the test module labels Jan 11, 2019
@spring-projects-issues spring-projects-issues removed the type: bug A general bug label Jan 12, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: test Issues in the test module status: invalid An issue that we don't feel is valid
Projects
None yet
Development

No branches or pull requests

2 participants