0

I find myself in a situation where I do not know the test code answers. I have an Impl with a single method that looks for a patient, but inside that method I have another external class that calls a method that also calls an interface to receive the response. I expose my code

IMPL

@Autowired
    private JwtServices jwtServices;
@Override
    public Map<String, Object> findByPatient(String identificationNumber, String birthdate, Boolean allowPatientData)  {

        Map<String, String> jwtData = jwtServices.getDecoderJwt();

        Map<String, String> dataPatient =  new HashMap<>();
        dataPatient.put("identificationNumber", identificationNumber);
        dataPatient.put("birthdate",birthdate);
        dataPatient.put("allowPatientData",allowPatientData.toString());
        dataPatient.put("appointmentId",jwtData.get("sub"));

        return this.apiFaroConfig.findPatient(dataPatient);
    }

My code JwtServices:

 @Autowired
    private JWTDecoder jwtDecoder;

    public Map<String, String> getDecoderJwt(){
        Map<String, String> jwtData = new HashMap<>();

        Payload payloadHeaderAuthorization = jwtDecoder.getPayloadAuthorization();

        jwtData.put("iss", payloadHeaderAuthorization.getIss());
        jwtData.put("sub",  payloadHeaderAuthorization.getSub());

        return jwtData;
    }

My test IMPL:

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class PatientServicesImplTest {

    @Mock
    private FaroApiRest apiFaroRest;

    @InjectMocks
    private JwtServices jwtServices ;

    @Mock
    private JWTDecoder jwtDecoder;

    @Mock
    private Payload payload;

    @InjectMocks
    private PatientServicesImpl patientServices;

    //Initialize variable
    private Map<String, String> dataPatient;
    private Map<String, Object> dataResponse;
    private Map<String, String> jwtData ;

    @BeforeEach
    void setUp() {
        //Initialize instances
        patientServices = new PatientServicesImpl(apiFaroRest);
        dataPatient = new HashMap<>();
        dataResponse = new HashMap<>();
        jwtData = new HashMap<>();

        //initialize dataPatient
        dataPatient.put("identificationNumber", "XXXX");
        dataPatient.put("birthdate","XXXX");
        dataPatient.put("allowPatientData", "true");
        dataPatient.put("appointmentId","XXX");

        //Initialize dataResponse status->200 ok
        dataResponse.put("datosPersonales", "XXX");
        dataResponse.put("anamnesis", "XXX");
        dataResponse.put("gdpr", "XXX");

        //Initialize data jwt
        jwtData.put("iss","560");
        jwtData.put("sub", "123456");

    }

    @Test
    void findByPatient() {
        //when

        //Jwt Services
        when(jwtDecoder.getPayloadAuthorization()).thenReturn(payload);

        when(jwtServices.getDecoderJwt()).thenReturn(jwtData);

        //PatientsImpl
        when(patientServices.findByPatient("XXXX", "XXXX", true)).thenReturn(dataResponse);
        when(apiFaroRest.findPatient(dataPatient)).thenReturn(dataResponse);

        //given
        Map<String, Object> response = apiFaroRest.findPatient(dataPatient);

        //then
        assertTrue(response.containsKey("datosPersonales"));
        assertNotNull(response);
       }

}

Error:

org.mockito.exceptions.misusing.WrongTypeOfReturnValue: 
HashMap cannot be returned by getSub()
getSub() should return String
***
If you're unsure why you're getting above error read on.
Due to the nature of the syntax above problem might occur because:
1. This exception *might* occur in wrongly written multi-threaded tests.
   Please refer to Mockito FAQ on limitations of concurrency testing.
2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies - 
   - with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.

I don't understand what's going on. I don't understand why I get that return change when I'm specifying its return. Then, if I remove the jwtDecoder it returns null jwtService because it tries to call the jwtDecoder method and it doesn't "exist" but of course, if what I want is that the JwtServices checks its getDecoder method and I return it the Map<String, String> so that the main IMPL method can give me the OK.

Morante
  • 1
  • 3
  • 1
    The error is complaining about a mock on `getSub()`, and I don't see the code for that. – Jorn Aug 03 '23 at 07:04
  • What jwtDecoder does is to bring a Payload (class), where it facilitates the extraction of the http Authorization headers.As you can see in the JwtServices class, I make that call and with the data of the header I extract what I am interested in, in this case the Sub and the Iss of the JWT. what happens that when making the call, as much as I make it mock is like that it does not falsify the method and what I obtain is null, but on the other hand I do not know why it identifies the HashMap, and sometimes I receive error of getSub and others of the variable payloadHeaderAuthorization is null... – Morante Aug 03 '23 at 07:30
  • `@InjectMocks` does _not_ create a mock. `@Mock` creates a mock. `@InjectMocks` creates a real instance. – knittl Aug 03 '23 at 08:23

2 Answers2

0

I am not sure if the mocks are being initiated properly.

Try adding runwith annotation.

@RunWith(MockitoJunitRunner.class)

OR

in the setup() method add MockitoAnnotations.initMocks()

Check this ans: https://stackoverflow.com/a/15494996

Harish
  • 1
  • 3
  • Thanks for the answer, I had already looked at this possible solution before, but neither with runWith, nor with InitMock nor even putting the mock(Entity.class) works... I don't know in which part I could take this action. – Morante Aug 03 '23 at 08:06
  • These are obsolete ways, he has the recent `@ExtendWith(MockitoExtension.class)` of the modern extension API – kan Aug 03 '23 at 08:21
  • I managed to simulate the data, with @InjectMock in patientImpl and declaring inside the method MockitoAnotation.openMock(this) – Morante Aug 03 '23 at 09:42
0

Your jwtServices is not a mock, but you are mocking it with when. Hence it's all confused and goes awry.

Overall, your test does not look as a properly designed. Usually it should be a single class under test, with all its dependencies mocked. You have a weird mix.

kan
  • 28,279
  • 7
  • 71
  • 101
  • My jwtServices needs to be mocked, since patientImpl uses a method of the JwtServices class, if I don't mock it, I get jwtServices is null error. – Morante Aug 03 '23 at 08:23
  • 1
    But you use @InjectMocks for it, hence it's not a @Mock, so you should not use it in `when`. – kan Aug 03 '23 at 09:08
  • Of course, if I do not use the when, when it passes through patientImpl it calls jwtServices and it returns null. now, if I create the Mock of jwtServices and initialize it in setUp as : jwtServices = new JwtServices()... – Morante Aug 03 '23 at 09:24
  • I can make a mock of the same mock, but jwtServices makes a call to interface jwtDecoder that returns a Payload.class with the header data. jwtServices.getDecodeJwt() calls the interface method and this one returns null because jwtDecoder is null. – Morante Aug 03 '23 at 09:24
  • 1
    @Morante your impl which you are testing depends on `jwtServices` and `apiFaroConfig`, nothing else. Hence these two fields should be annotated with `@Mock`. Internals of the `JwtServices` are irrelevant and unrelated as they are mocked, hence `jwtDecoder` and related should not even be part of your test. – kan Aug 03 '23 at 09:32
  • `jwtServices = new JwtServices()` makes no sense. It should be just declared with `@Mock` – kan Aug 03 '23 at 09:34
  • I managed to simulate the data, with @InjectMock in patientImpl and declaring inside the method MockitoAnotation.openMocks(this) – Morante Aug 03 '23 at 09:42