【问题标题】:Unable to mock RestTemplate in SpringBoot-Junit5-Mockito无法在 Spring Boot-Junit 5-Mockito 中模拟 RestTemplate
【发布时间】:2019-07-01 23:01:42
【问题描述】:

我正在尝试在我的 DAO 类中模拟一个休息模板,但 Mockito 抛出一个奇怪的错误,说它无法模拟。

尝试涵盖我的 Spring Boot 应用程序 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

【问题讨论】:

    标签: java spring-boot mockito resttemplate junit5


    【解决方案1】:

    所描述(或从属)问题的一个原因可能是,因为RestTemplate 既不是“私有”也不是“最终”和“称为可模拟”,restTemplate.getForEntity() 的调用/模拟

    ...在current version中,此方法有三种风格/带有重载参数:

    • ... getForEntity(String url, Class&lt;T&gt; responseType, Object... uriVariables) ...
    • ... getForEntity(String url, Class&lt;T&gt; responseType, Map&lt;String, ?&gt; uriVariables) ...
    • ... getForEntity(URI url, Class&lt;T&gt; responseType) ...

    在您的(操作)代码中,您似乎使用了第一种风格,因此在不更改它(操作代码)的情况下,我建议将您的测试代码调整为:

    ...restTemplate.getForEntity(Mockito.anyString(), String.class
    /*!!!*/, ArgumentMatchers.<Object>any());
    

    另见:


    但错误消息仍然让我“谨慎”并想知道...您处于“最前沿”的 junit (5) 堆栈上,您对自己的测试设置有信心吗!? (缺少 gradle 库/配置?)

    请“试试”:

    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所示。 如果那(以及本教程中的其他发现)没有帮助,请考虑:

    报告(它)到邮件列表(。)


    我第一次听到/读到(在我的生命中):

    在我的 DAO 类中模拟 一个休息模板

    考虑将您的 dao 命名为“服务”(+ 相应的步骤/重构;)

    【讨论】:

      【解决方案2】:

      好的,我解决了这个问题。这是 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

      【讨论】:

        【解决方案3】:

        这个问题发生在我身上,因为我无意中运行了 Java 11。一旦我切换到项目的标准 Java 8,问题就消失了。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2022-01-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-05-01
          相关资源
          最近更新 更多