-
Notifications
You must be signed in to change notification settings - Fork 38.1k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Mockito cannot mock AspectJ-weaved @Async method #24724
Comments
Due to the involvement of @philwebb and @wilkinsona, have you come across something like this before? |
We've seen a few issue like this in the past related to AOP. I'll transfer the issue. |
@cdalexndr Could you please provide a sample that shows the problem. Preferably something we can just download or clone and run. |
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed. |
Test project at: https://github.com/cdalexndr/spring-boot-issue-17593 After some playing with various settings, I found three ways to cause the tests to pass and I've commented them in the code as:
|
Thanks for the sample @cdalexndr. @philwebb @sbrannen This doesn't seem to be Spring Boot specific as using regular mocks and removing |
@mbhave I've tried and failed to reproduce the problem without |
Sorry, @wilkinsona, I should've copied what I had in the comment above. I used the sample provided above and this is what I'd changed the test to: @ContextConfiguration(classes = Application.class)
public class Test extends AbstractTestNGSpringContextTests {
private static final Logger log = LoggerFactory.getLogger( Test.class );
@Spy
Service service;
@BeforeMethod
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@org.testng.annotations.Test
public void test() {
assertTrue( MockUtil.isSpy( service ) );
doReturn( new CompletableFuture<>() ).when( service ).asyncMethod( any( Integer.class ) );
doReturn( null ).when( service ).work(); //commenting this line causes test to pass (3 of 3)
CompletableFuture<Integer> future = service.asyncMethod( 1 );
log.info( "Success" );
}
} |
The following reproduces the problem: package example;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import java.util.concurrent.CompletableFuture;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.internal.util.MockUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
@ContextConfiguration(classes = Application.class)
@RunWith(SpringRunner.class)
public class AsyncSpyTests {
private static final Logger log = LoggerFactory.getLogger(AsyncSpyTests.class);
@Spy
ExampleService service;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
public void test() {
assertThat(MockUtil.isSpy(this.service)).isTrue();
doReturn(new CompletableFuture<>()).when(this.service).asyncMethod(any(Integer.class ));
doReturn(null).when(this.service).work(); // commenting this line causes test to pass
this.service.asyncMethod(1);
log.info("Success");
}
}
@Service
class ExampleService {
private static final Logger log = LoggerFactory.getLogger( ExampleService.class );
@Async // commenting this line causes test to pass
public CompletableFuture<Integer> asyncMethod(Integer param) {
work();
return new CompletableFuture<>();
}
protected Integer work() {
log.info("Work");
return 0;
}
}
@EnableAutoConfiguration // commenting this line causes test to pass
@ComponentScan
@Configuration
@EnableAspectJAutoProxy
@EnableAsync(mode = AdviceMode.ASPECTJ)
class Application {
} plugins {
id 'org.springframework.boot' version '2.2.5.RELEASE'
}
apply plugin: 'java'
apply plugin: 'io.spring.dependency-management'
repositories {
mavenCentral()
}
dependencies {
implementation group: 'org.aspectj', name: 'aspectjweaver'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-aop'
implementation 'org.springframework:spring-aspects'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
File springAgent = configurations.testRuntimeClasspath.filter { f -> f.name.matches("aspectjweaver.*\\.jar") }.iterator().next();
jvmArgs += ["-javaagent:" + springAgent.absolutePath]
} Note that |
Here's a modified version of the code above that reproduces the problem without any involvement from Spring Boot. The key difference is that package example;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import java.util.concurrent.CompletableFuture;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.internal.util.MockUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
@ContextConfiguration(classes = Application.class)
@RunWith(SpringRunner.class)
public class AsyncSpyTests {
private static final Logger log = LoggerFactory.getLogger(AsyncSpyTests.class);
@Spy
ExampleService service;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
public void test() {
assertThat(MockUtil.isSpy(this.service)).isTrue();
doReturn(new CompletableFuture<>()).when(this.service).asyncMethod(any(Integer.class ));
doReturn(null).when(this.service).work(); // commenting this line causes test to pass
this.service.asyncMethod(1);
log.info("Success");
}
}
@Service
class ExampleService {
private static final Logger log = LoggerFactory.getLogger( ExampleService.class );
@Async // commenting this line causes test to pass
public CompletableFuture<Integer> asyncMethod(Integer param) {
work();
return new CompletableFuture<>();
}
protected Integer work() {
log.info("Work");
return 0;
}
}
@ComponentScan
@Configuration
@EnableAspectJAutoProxy
@EnableAsync(mode = AdviceMode.ASPECTJ)
class Application {
@Bean
TaskExecutor taskExector() {
return new ThreadPoolTaskExecutor();
}
} |
Still reproductible with Spring 5.3.3 (re-encountered the bug) |
This looks like a more general problem, see #24735 (comment) that has a reproducer for |
This comment was marked as duplicate.
This comment was marked as duplicate.
Hello, we have this issue too. |
When trying to mock an
@Async
method from a@SpyBean
I get the following error:This error only appears when I run the test without debugger attached. When I try to debug, the error doesn't show up, and everything works fine.
When I remove the
@Async
, then it works also without the debugger attached.This error started showing up after I upgraded from spring 4 to spring 5.
Using spring-boot-starter-test 2.1.6.RELEASE (mockito-core:2.23.4, spring-test:5.1.8.RELEASE)
The text was updated successfully, but these errors were encountered: