admin管理员组

文章数量:1124660

In the setup in my test, the line:

when(kc.getToken()).thenReturn(token);   

does not work and I get the following error from the ServiceImplementation:

java.lang.NullPointerException: Cannot invoke "services.KeycloakService.getToken()" because "this.kc" is null

I am using the following dependencies:

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.mockito:mockito-core:5.14.2'
testImplementation 'org.mockito:mockito-junit-jupiter:5.14.2'

WHAT I TRIED:

  • run the testMockInjection: all tests are green.
  • run the testUpdate: I see that the code from the Service implementation stops in the line
String zaToken = kc.getToken

and gives the aforementioned error

  • the message in the set up is printed as expected
  • I tried using a spy instead of a mock but it did not work again. Also, as I understand, It is better to use the mock because I want to 'fake' the whole service and need to stub just one function.
  • Test import is the correct one
  • I am using @ExtendWith(MockitoExtension.class), @BeforeEach and @InjectMocks which work in my other tests.

However, I also tried MockitoAnnotations.initMocks(this) and the error remained.

SERVICE IMPLEMENTATION CODE

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
...
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
...
import services.AasService;
import services.KeycloakService;
 
@Service
public class AasServiceImpl implements AasService{
    private final RestTemplate restTemplate;
    
    @Autowired
    private KeycloakService kc;

    @Value("${ass.server}")
    private String aasUri;
    @Value("${ass.id}")
    private String aasIdShort;
    
    public AasServiceImpl (RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }
    
    @Override
    public void update(String applicationUri, String nodeId, JSONObject value) {
        System.out.println(applicationUri);
        System.out.println(nodeId);
        System.out.println(value);

        String submodelElementValueId = applicationUri.replace(":", "_") + nodeId.replace("i=", "");
        
        System.out.println(submodelElementValueId);
        
        if (kc instanceof KeycloakServiceImpl) {
            System.out.println("This is original service.");
        } else {
            System.out.println("This is  not the original service.");
//this is the option printed 

        }
//problematic line is the following                 
        String zaToken = kc.getToken();
        
//this is not printed in the code 
        System.out.println(zaToken);
        try {
            updateValue("DefectDetectionSkill", "Inputs", submodelElementValueId, value, zaToken);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
    }
}

TEST CODE:

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
...
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
...
import org.junit.jupiter.api.BeforeEach;
import services.KeycloakService;  //this is an interface

@ExtendWith(MockitoExtension.class)
public class AasServiceImplTest {

    @Mock
    private KeycloakService kc;
    
    @Mock
    private RestTemplate restTemplate;
        
    @InjectMocks
    private AasServiceImpl aasService;

    @BeforeEach
    void setUp() {
       
        String token = "mockToken";
        when(kc.getToken()).thenReturn(token);

        ResponseEntity<String> response= new ResponseEntity<>("", HttpStatus.OK);
        when(restTemplate.exchange(anyString(), any(), any(), eq(String.class))).thenReturn(response);
 
        System.out.println("set up runs");
        //this message is printed
    }

    /*
    @Test
    void testMockInjection() {
        
        assertNotNull(kc, "KeycloakService mock is not injected!");
        assertNotNull(restTemplate, "RestTemplate mock is not injected!");
        assertNotNull(aasService, "AasServiceImpl is not initialized!");
    }
    
    */
    @Test
    void testUpdate() {
        
        //Arrange
        String applicationUri = "http://mock_uri";
        String nodeId="i=ioanna";
        JSONObject value = new JSONObject();
        value.put("Value", 1234);

        //Act
        aasService.update(applicationUri,  nodeId , value);
        System.out.println("Value: " + value.get("Value"));

        //Assert
        assertNotNull(kc, "KeycloakService mock is not injected!");

        //verify(kc, times(1)).getToken();
        //verify(restTemplate, atLeastOnce()).exchange(anyString(), any(), any(), eq(String.class));
    }

In the setup in my test, the line:

when(kc.getToken()).thenReturn(token);   

does not work and I get the following error from the ServiceImplementation:

java.lang.NullPointerException: Cannot invoke "services.KeycloakService.getToken()" because "this.kc" is null

I am using the following dependencies:

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.mockito:mockito-core:5.14.2'
testImplementation 'org.mockito:mockito-junit-jupiter:5.14.2'

WHAT I TRIED:

  • run the testMockInjection: all tests are green.
  • run the testUpdate: I see that the code from the Service implementation stops in the line
String zaToken = kc.getToken

and gives the aforementioned error

  • the message in the set up is printed as expected
  • I tried using a spy instead of a mock but it did not work again. Also, as I understand, It is better to use the mock because I want to 'fake' the whole service and need to stub just one function.
  • Test import is the correct one
  • I am using @ExtendWith(MockitoExtension.class), @BeforeEach and @InjectMocks which work in my other tests.

However, I also tried MockitoAnnotations.initMocks(this) and the error remained.

SERVICE IMPLEMENTATION CODE

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
...
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
...
import services.AasService;
import services.KeycloakService;
 
@Service
public class AasServiceImpl implements AasService{
    private final RestTemplate restTemplate;
    
    @Autowired
    private KeycloakService kc;

    @Value("${ass.server}")
    private String aasUri;
    @Value("${ass.id}")
    private String aasIdShort;
    
    public AasServiceImpl (RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }
    
    @Override
    public void update(String applicationUri, String nodeId, JSONObject value) {
        System.out.println(applicationUri);
        System.out.println(nodeId);
        System.out.println(value);

        String submodelElementValueId = applicationUri.replace(":", "_") + nodeId.replace("i=", "");
        
        System.out.println(submodelElementValueId);
        
        if (kc instanceof KeycloakServiceImpl) {
            System.out.println("This is original service.");
        } else {
            System.out.println("This is  not the original service.");
//this is the option printed 

        }
//problematic line is the following                 
        String zaToken = kc.getToken();
        
//this is not printed in the code 
        System.out.println(zaToken);
        try {
            updateValue("DefectDetectionSkill", "Inputs", submodelElementValueId, value, zaToken);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
    }
}

TEST CODE:

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
...
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
...
import org.junit.jupiter.api.BeforeEach;
import services.KeycloakService;  //this is an interface

@ExtendWith(MockitoExtension.class)
public class AasServiceImplTest {

    @Mock
    private KeycloakService kc;
    
    @Mock
    private RestTemplate restTemplate;
        
    @InjectMocks
    private AasServiceImpl aasService;

    @BeforeEach
    void setUp() {
       
        String token = "mockToken";
        when(kc.getToken()).thenReturn(token);

        ResponseEntity<String> response= new ResponseEntity<>("", HttpStatus.OK);
        when(restTemplate.exchange(anyString(), any(), any(), eq(String.class))).thenReturn(response);
 
        System.out.println("set up runs");
        //this message is printed
    }

    /*
    @Test
    void testMockInjection() {
        
        assertNotNull(kc, "KeycloakService mock is not injected!");
        assertNotNull(restTemplate, "RestTemplate mock is not injected!");
        assertNotNull(aasService, "AasServiceImpl is not initialized!");
    }
    
    */
    @Test
    void testUpdate() {
        
        //Arrange
        String applicationUri = "http://mock_uri";
        String nodeId="i=ioanna";
        JSONObject value = new JSONObject();
        value.put("Value", 1234);

        //Act
        aasService.update(applicationUri,  nodeId , value);
        System.out.println("Value: " + value.get("Value"));

        //Assert
        assertNotNull(kc, "KeycloakService mock is not injected!");

        //verify(kc, times(1)).getToken();
        //verify(restTemplate, atLeastOnce()).exchange(anyString(), any(), any(), eq(String.class));
    }
Share Improve this question edited yesterday Mureinik 310k54 gold badges346 silver badges385 bronze badges asked 2 days ago joanjoan 211 silver badge1 bronze badge New contributor joan is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct. 4
  • 2 seems the "when" is not really the problem, it's because that 'kc' variable wasn't instantiated. add a breakpoint, debug, and check if any of the fields are instantiated. – Stultuske Commented 2 days ago
  • 1 Just a thought, but I'm wondering if @InjectMocks sees that there is a mock to be injected via constructor, and then it injects only that, instead of also injecting kc via field injection. Does @InjectMocks use all injection types, or just the first one it finds (i.e. it can do constructor injection and therefore only does that)? – k314159 Commented 2 days ago
  • 2 IMO Best practice is to make all injected fields constructor parameters. I find it particularly confusing to have some injected via constructor and some injected via magic (Spring's AOP/bytecode manipulation). – mikeb Commented 2 days ago
  • This question is similar to: Mockito injection not working for constructor AND setter mocks together. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem. – k314159 Commented yesterday
Add a comment  | 

1 Answer 1

Reset to default 1

It seems like @InjectMock is failing to mix field injection and constructor argument injection. Either have both the restTemplate and kc fields autowired:

@Service
public class AasServiceImpl implements AasService {
    // No explicit constructor!

    @Autowired
    private  RestTemplate restTemplate;

    @Autowired
    private KeycloakService kc;
    ...

Or have them both as constructor arguments:

@Service
public class AasServiceImpl implements AasService {
    private  RestTemplate restTemplate;
    private KeycloakService kc;


    public AasServiceImpl (RestTemplate restTemplate, KeycloakService kc) {
        this.restTemplate = restTemplate;
        this.kc = kc;
    }
    ...

本文标签: junit testing error in java (eclipse) set up quotwhenquot does not workStack Overflow