无法在 Spring Boot-Junit 5-Mockito 中模拟 RestTemplate
Unable to mock RestTemplate in SpringBoot-Junit5-Mockito
我试图在我的 DAO 中模拟一个 rest 模板 class 但 Mockito 抛出奇怪的错误,说它无法模拟。
正在尝试涵盖我的 Spring 启动应用程序版本 2.x 的单元测试用例。我几乎尝试了互联网上所有可能的解决方案,例如更新 JDK/JRE 进行编译,但我遇到以下错误:
org.mockito.exceptions.base.MockitoException:
Mockito cannot mock this class: class org.springframework.web.client.RestTemplate.
Mockito can only mock non-private & non-final classes.
If you're not sure why you're getting this error, please report to the mailing list.
Java : 1.8
JVM vendor name : Oracle Corporation
JVM vendor version : 25.181-b13
JVM name : Java HotSpot(TM) 64-Bit Server VM
JVM version : 1.8.0_181-b13
JVM info : mixed mode
OS name : Windows 10
OS version : 10.0
Underlying exception : java.lang.IllegalArgumentException: Could not create type
at org.mockito.junit.jupiter.MockitoExtension.beforeEach(MockitoExtension.java:115)
....
以下是我的代码:
build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-data-rest'
implementation 'org.springframework.retry:spring-retry'
implementation 'org.aspectj:aspectjrt'
implementation 'org.aspectj:aspectjweaver'
implementation 'org.springframework:spring-aop'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.2.0'
testCompile 'org.junit.jupiter:junit-jupiter-params:5.2.0'
testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.2.0'
testImplementation 'org.mockito:mockito-core:2.+'
testImplementation 'org.mockito:mockito-junit-jupiter:2.18.3'
}
test {
testLogging.showStandardStreams = true
useJUnitPlatform()
}
MyDao.java
@Repository
public class MyDao {
@Value("${app.prop.service.url}")
private String url;
@Autowired
public RestTemplate restTemplate;
public String getSignals() {
System.out.println("url -----------------------> " + url);
return new RetryTemplate().execute(context -> {
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
if (response.getStatusCodeValue() == 200) {
System.out.println("Server response -> " + response.getBody());
return response.getBody();
} else {
throw new RuntimeException("server response status: " + response.getStatusCode());
}
}, context -> {
System.out.println("retry count: " + context.getRetryCount());
System.err.println("error -> " + context.getLastThrowable());
return null;
});
}
}
MyDaoTest.java
@ExtendWith(MockitoExtension.class)
public class MyDaoTest {
@Mock
private RestTemplate restTemplate;
@InjectMocks
private MyDao dao;
@BeforeEach
public void prepare() {
ResponseEntity<String> response = new ResponseEntity<>("{name: myname}", HttpStatus.OK);
Mockito.doReturn(response).when(restTemplate.getForEntity(Mockito.anyString(), String.class));
}
@Test
public void testGetSignals() {
System.out.println("------------------TEST-------------------");
String result = dao.getSignals();
System.out.println("result ------>" + result);
assertEquals("{name: myname}", result);
}
}
RestTemplate 的 BeanConfig
@Bean
public RestTemplate restTemplate() {
// block of code for SSL config for HTTPS connection
return new RestTemplate();
}
任何建议都会很有帮助
P.S:应用程序 运行 通过 gradle 命令完全正常
gradlew bootRun
问题仅在于单元测试
gradlew test
所描述(或从属)问题的一个原因可能是,因为 RestTemplate
既不是 "private" 也不是 "final" 和 "known as mockable",invocation/mocking 共 restTemplate.getForEntity()
...在current version中,此方法可用三个flavors/with重载参数:
... getForEntity(String url, Class<T> responseType, Object... uriVariables) ...
... getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables) ...
... getForEntity(URI url, Class<T> responseType) ...
在你的(操作)代码中,你似乎使用了第一种风格,所以在不改变它(操作代码)的情况下,我建议将你的测试代码调整为:
...restTemplate.getForEntity(Mockito.anyString(), String.class
/*!!!*/, ArgumentMatchers.<Object>any());
另请参阅:
- Mockito not working for RestTemplate
- How to properly match varargs in Mockito
但错误消息仍然让我 "cautious" 想知道...你在 "beeding edge" junit (5) 堆栈上,你对你的测试设置有信心吗!? (缺少 gradle libs/config?)
拜托,"try":
testCompile "org.mockito:mockito-core:2.+"
testCompile('org.mockito:mockito-junit-jupiter:2.18.3')
不是:
...
testImplementation 'org.mockito:mockito-core:2.+'
testImplementation 'org.mockito:mockito-junit-jupiter:2.18.3'
..如 dzone's spring-boot-2-with-junit-5-and-mockito-2-for-unit 所示。
如果那(以及教程中的其他发现)没有帮助,请考虑真的:
report (it) to the mailing list(.)
而我 heard/read 第一次(在我的生命中):
mock a rest template in my DAO class
考虑将您的 dao 命名为 "service"(+ 根据 steps/refactorings;)
好的,我解决了这个问题。这是 mockito-core 和 mockito-junit-jupiter jar 之间的版本不匹配,导致了这个问题。
正确的依赖关系是:
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.2.0'
testCompile 'org.junit.jupiter:junit-jupiter-params:5.2.0'
testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.2.0'
testImplementation 'org.mockito:mockito-core:2.18.3'
testImplementation 'org.mockito:mockito-junit-jupiter:2.18.3'
之前是
testImplementation 'org.mockito:mockito-core:2.+'
Gradle 正在选择最新版本的 mockito-core jar,因为它被要求选择构建文件中定义的 2.x 系列中的任何版本。我相信其余的是不言自明的。
快乐,单元测试! :P
这个问题发生在我身上是因为我不小心运行 Java 11。一旦我切换到项目标准的Java 8,问题就消失了。
我试图在我的 DAO 中模拟一个 rest 模板 class 但 Mockito 抛出奇怪的错误,说它无法模拟。
正在尝试涵盖我的 Spring 启动应用程序版本 2.x 的单元测试用例。我几乎尝试了互联网上所有可能的解决方案,例如更新 JDK/JRE 进行编译,但我遇到以下错误:
org.mockito.exceptions.base.MockitoException:
Mockito cannot mock this class: class org.springframework.web.client.RestTemplate.
Mockito can only mock non-private & non-final classes.
If you're not sure why you're getting this error, please report to the mailing list.
Java : 1.8
JVM vendor name : Oracle Corporation
JVM vendor version : 25.181-b13
JVM name : Java HotSpot(TM) 64-Bit Server VM
JVM version : 1.8.0_181-b13
JVM info : mixed mode
OS name : Windows 10
OS version : 10.0
Underlying exception : java.lang.IllegalArgumentException: Could not create type
at org.mockito.junit.jupiter.MockitoExtension.beforeEach(MockitoExtension.java:115)
....
以下是我的代码:
build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-data-rest'
implementation 'org.springframework.retry:spring-retry'
implementation 'org.aspectj:aspectjrt'
implementation 'org.aspectj:aspectjweaver'
implementation 'org.springframework:spring-aop'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.2.0'
testCompile 'org.junit.jupiter:junit-jupiter-params:5.2.0'
testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.2.0'
testImplementation 'org.mockito:mockito-core:2.+'
testImplementation 'org.mockito:mockito-junit-jupiter:2.18.3'
}
test {
testLogging.showStandardStreams = true
useJUnitPlatform()
}
MyDao.java
@Repository
public class MyDao {
@Value("${app.prop.service.url}")
private String url;
@Autowired
public RestTemplate restTemplate;
public String getSignals() {
System.out.println("url -----------------------> " + url);
return new RetryTemplate().execute(context -> {
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
if (response.getStatusCodeValue() == 200) {
System.out.println("Server response -> " + response.getBody());
return response.getBody();
} else {
throw new RuntimeException("server response status: " + response.getStatusCode());
}
}, context -> {
System.out.println("retry count: " + context.getRetryCount());
System.err.println("error -> " + context.getLastThrowable());
return null;
});
}
}
MyDaoTest.java
@ExtendWith(MockitoExtension.class)
public class MyDaoTest {
@Mock
private RestTemplate restTemplate;
@InjectMocks
private MyDao dao;
@BeforeEach
public void prepare() {
ResponseEntity<String> response = new ResponseEntity<>("{name: myname}", HttpStatus.OK);
Mockito.doReturn(response).when(restTemplate.getForEntity(Mockito.anyString(), String.class));
}
@Test
public void testGetSignals() {
System.out.println("------------------TEST-------------------");
String result = dao.getSignals();
System.out.println("result ------>" + result);
assertEquals("{name: myname}", result);
}
}
RestTemplate 的 BeanConfig
@Bean
public RestTemplate restTemplate() {
// block of code for SSL config for HTTPS connection
return new RestTemplate();
}
任何建议都会很有帮助
P.S:应用程序 运行 通过 gradle 命令完全正常
gradlew bootRun
问题仅在于单元测试
gradlew test
所描述(或从属)问题的一个原因可能是,因为 RestTemplate
既不是 "private" 也不是 "final" 和 "known as mockable",invocation/mocking 共 restTemplate.getForEntity()
...在current version中,此方法可用三个flavors/with重载参数:
... getForEntity(String url, Class<T> responseType, Object... uriVariables) ...
... getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables) ...
... getForEntity(URI url, Class<T> responseType) ...
在你的(操作)代码中,你似乎使用了第一种风格,所以在不改变它(操作代码)的情况下,我建议将你的测试代码调整为:
...restTemplate.getForEntity(Mockito.anyString(), String.class
/*!!!*/, ArgumentMatchers.<Object>any());
另请参阅:
- Mockito not working for RestTemplate
- How to properly match varargs in Mockito
但错误消息仍然让我 "cautious" 想知道...你在 "beeding edge" junit (5) 堆栈上,你对你的测试设置有信心吗!? (缺少 gradle libs/config?)
拜托,"try":
testCompile "org.mockito:mockito-core:2.+"
testCompile('org.mockito:mockito-junit-jupiter:2.18.3')
不是:
...
testImplementation 'org.mockito:mockito-core:2.+'
testImplementation 'org.mockito:mockito-junit-jupiter:2.18.3'
..如 dzone's spring-boot-2-with-junit-5-and-mockito-2-for-unit 所示。 如果那(以及教程中的其他发现)没有帮助,请考虑真的:
report (it) to the mailing list(.)
而我 heard/read 第一次(在我的生命中):
mock a rest template in my DAO class
考虑将您的 dao 命名为 "service"(+ 根据 steps/refactorings;)
好的,我解决了这个问题。这是 mockito-core 和 mockito-junit-jupiter jar 之间的版本不匹配,导致了这个问题。
正确的依赖关系是:
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.2.0'
testCompile 'org.junit.jupiter:junit-jupiter-params:5.2.0'
testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.2.0'
testImplementation 'org.mockito:mockito-core:2.18.3'
testImplementation 'org.mockito:mockito-junit-jupiter:2.18.3'
之前是
testImplementation 'org.mockito:mockito-core:2.+'
Gradle 正在选择最新版本的 mockito-core jar,因为它被要求选择构建文件中定义的 2.x 系列中的任何版本。我相信其余的是不言自明的。
快乐,单元测试! :P
这个问题发生在我身上是因为我不小心运行 Java 11。一旦我切换到项目标准的Java 8,问题就消失了。