2

I'm trying to mock a rest template in my DAO class but Mockito throws weird error saying it isn't able to mock.

Trying to cover unit test cases for my Spring boot app version 2.x. I have almost tried all possible solutions over the internet like updating JDK/JRE for compilation but I'm stuck with the following error:

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)
....

Following is my code:

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);
    }
}

BeanConfig for RestTemplate

@Bean
public RestTemplate restTemplate() {
    // block of code for SSL config for HTTPS connection
    return new RestTemplate();
}

Any suggestions will be really helpful

P.S: The application is running perfectly fine through gradle command

gradlew bootRun

The problem is only with unit testing

gradlew test
Shravan
  • 1,182
  • 8
  • 21

3 Answers3

2

One cause of the described (or a subordinate) problem might be, since RestTemplate is nor "private" nor "final" and "known as mockable", the invocation/mocking of restTemplate.getForEntity()

... in the current version, this method is available in three flavors/with overloading parameters:

  • ... getForEntity(String url, Class<T> responseType, Object... uriVariables) ...
  • ... getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables) ...
  • ... getForEntity(URI url, Class<T> responseType) ...

In your (operative) code you seem to use the 1st flavor, so without changing it (the operative code), I propose to adjust your test code to:

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

See also:

xerx593
  • 12,237
  • 5
  • 33
  • 64
2

This issue happened to me because I was inadvertently running Java 11. Once I switched to Java 8 which is standard for the project, the issue went away.

SpareTheRod
  • 476
  • 6
  • 13
1

Okay, I solved the problem. It was a version mismatch between mockito-core and mockito-junit-jupiter jars, which was causing thr problem.

The correct dependency is:

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'

Earlier, it was

testImplementation 'org.mockito:mockito-core:2.+'

Gradle was picking the latest version of mockito-core jar since it was asked to pick any version in the 2.x series, as defined in the build file. I believe the rest is self explanatory.

Happy, Unit testing! :P

Shravan
  • 1,182
  • 8
  • 21