pchudzik / springmock

alternative spring mocking infrastructure
https://blog.pchudzik.com/201707/springmock-v1/
MIT License
22 stars 4 forks source link

Allow to leave spring proxy as it is when creating spies #16

Closed pchudzik closed 6 years ago

pchudzik commented 7 years ago

In case of asynchronous code @Async you might want to leave default behavior and do not unwrap created proxy.

fernandocamargoai commented 6 years ago

@pchudzik Unwrapping proxies is really problematic when using Spring Batch. I have Step Scoped beans behind a proxy. There's the StepScopeTestExecutionListener which prepares the scope, but it runs on prepareTestInstance(). At that point, Spring Mock had already unwrapped a proxy with the Step Scoped bean and an exception was thrown.

pchudzik commented 6 years ago

@fernandocamargoti

I've just checked something and it turns out you will not be able to change behavior of this bean:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
public class SpyOnProxy {
    @Autowired
    Service service;

    @Test
    public void should_inject_async_service_as_a_synchronous_mock() {
        //given
        Service spy = Mockito.spy(service);

        //when
        final String res = spy.hello();

        //then
        Mockito.verify(spy).hello();
    }

    @Test(expected = ExpectedException.class)
    //this one fails!!
    public void can_not_change_proxy_behaviour() {
        //given
        Service spy = Mockito.spy(service);
//      Mockito.when(spy.hello()).thenThrow(new ExpectedException());
        Mockito.doThrow(new ExpectedException()).when(spy).hello();

        //when
        spy.hello();
    }

    @Configuration
    @EnableAsync
    static class Config {
        @Bean
        Service service() {
            return new Service();
        }
    }

    static class Service {
        @Async
        public String hello() {
            return "hello";
        }
    }

    private static class ExpectedException extends RuntimeException {
    }
}

Can you create sample project so I will see what's going on in there or at least provide stacktrace?

Have you checked if it is working correctly with @SpyBean from spring-boot?

I'd rather not implement something which is broken by design ;) If @SpyBean will be working I'll take a look on what is going on in there.