Kwinto0123
Kwinto0123

Reputation: 91

Strict stubbing argument mismatch when use @Value

any() matcher for @Value field gives Strict stubbing argument mismatch error.

Is there any ArgumentMatcher proper to @Value field? And maybe someone knows why 'any()' is not working?

Field (sampleVersion), which I get from application.properties using @Value annotation, has in test value null (proof - first line of logs). Even logs show that there is null and matcher expected null.

PS I solved this problem using this:

ReflectionTestUtils.setField(someClass, "serviceUrl", "value");

but I'm curious is it necessary and why simple any() doesn't work.

Logs:

sampleVersion has value: null

Strict stubbing argument mismatch. Please check:
 - this invocation of 'postForEntity' method:
    restTemplate.postForEntity(
    null,
    <{key=null},[Content-Type:"application/json"]>,
    class java.lang.String
);
    -> at com.some.package.SomeClass.someMethod(SomeClass.java:126)
 - has following stubbing(s) with different arguments:
    1. restTemplate.postForEntity(null, null, null);
      -> at com.some.package.SomeClassTest.someTest(SomeClassTest.java:59)

Method:

    @Value("${sample.version}")
    private String sampleVersion;

    public ResponseEntity someMethod()  {

    System.out.println("sampleVersion has value: " + sampleVersion);

    Map<String, Object> paramMap = new HashMap<>();
    paramMap.put("key", sampleVersion);

    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);

    return restTemplate.postForEntity(sampleVersion, new HttpEntity<>(paramMap, headers), String.class);
    }

Test:

    @InjectMocks
    SomeClass someClass = new SomeClass();

    @Test
    void someTest() {
    //given
    ResponseEntity response = new ResponseEntity("{ \"text\" : \"hello\"}", HttpStatus.OK);

    //when
    when(restTemplate.postForEntity(any(), any(), eq(String.class))).thenReturn(response);
    ResponseEntity responseEntity = someClass.someMethod();

    assertNotNull(responseEntity);
    }

Environment:

Java 11
SpringBoot 2.1.4
Mockito 2.27.0
Jupiter 5.3.2

Upvotes: 2

Views: 2809

Answers (2)

Christopher Diep
Christopher Diep

Reputation: 51

I'm writing this into the answers because the author put the solution into the question. (This is for better visibility.) The author was able to resolve the issue using

ReflectionTestUtils.setField(someClass, "field", "value");

This can assign the private field a value for testing purposes. The value is not assigned by the @Value annotation during the automated test.

For more information on usage: https://www.baeldung.com/spring-reflection-test-utils#using-reflectiontestutils-to-set-a-value-of-a-non-public-field

Upvotes: 0

Lesiak
Lesiak

Reputation: 26084

There are 2 overloads of postForEntity that could take 3 parameters:

  • <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
  • <T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType)

In your production code, you are calling the first one.

In your test, you stub the second one instead. This stems from the fact that in your call:

when(restTemplate.postForEntity(any(), any(), eq(String.class))).thenReturn(response);

You provide 3 arguments, and the values provided match types of parameters of the second overload. Thus the overload with varargs is not even considered. (any() returns null, which is a good match for both URI and String).

Instead of reflection, I would advise to:

  • use constructor injection for sampleVersion
  • drop @InjectMocks on the service under test
  • instead, construct service under test in a BeforeEach method.

Upvotes: 1

Related Questions