@RunWith(MockitoJUnitRunner.class) 模拟为空
@RunWith(MockitoJUnitRunner.class) mocks are null
我正在使用“RunWith(MockitoJUnitRunner.class)”、@Mock notationt 来模拟服务,并使用@InjectMocks notation 将模拟服务注入控制器。我在“何时”的 ChargingStationsControllerTest:40 中得到 NullPointerException。我调试并意识到模拟是空的。
这是我的代码:
package com.example.assignmentchargingstations.controllers;
import com.example.assignment.chargingStations.controllers.ChargingStationsController;
import com.example.assignment.chargingStations.models.AddressInfo;
import com.example.assignment.chargingStations.models.ChargingStation;
import com.example.assignment.chargingStations.models.StatusType;
import com.example.assignment.chargingStations.services.OpenChargeMapService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import java.util.ArrayList;
import java.util.List;
import static org.mockito.ArgumentMatchers.anyDouble;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class ChargingStationsControllerTest {
@Mock
private OpenChargeMapService openChargeMapService;
@InjectMocks
private ChargingStationsController chargingStationsController;
@Test
public void testGetNearestChargingStations() throws Exception {
Double latitude = 90.0;
Double longitude = 90.0;
List<ChargingStation> chargingStationsListExpected = new ArrayList<>();
StatusType statusTypeExpected = StatusType.builder().IsOperational(true).build();
AddressInfo addressInfoExpected = AddressInfo.builder().Latitude(latitude).Longitude(longitude).Title("Test Charging Station").build();
chargingStationsListExpected.add(ChargingStation.builder().StatusType(statusTypeExpected).AddressInfo(addressInfoExpected).build());
when(openChargeMapService.getNearestChargingStations(anyDouble(), anyDouble())).thenReturn(chargingStationsListExpected);
ResponseEntity<List<ChargingStation>> responseExpected = chargingStationsController.getNearestChargingStations(latitude, longitude);
Assertions.assertEquals(responseExpected.getStatusCode(), HttpStatus.OK);
Assertions.assertEquals(responseExpected.getBody(), chargingStationsListExpected);
}
}
我正在测试的控制器:
package com.example.assignment.chargingStations.controllers;
import com.example.assignment.chargingStations.models.ChargingStation;
import com.example.assignment.chargingStations.services.OpenChargeMapService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@Controller
public class ChargingStationsController {
private final OpenChargeMapService openChargeMapService;
public ChargingStationsController(OpenChargeMapService openChargeMapService) {
this.openChargeMapService = openChargeMapService;
}
@RequestMapping(path = "/nearest-charging-stations", method = RequestMethod.GET)
public ResponseEntity<List<ChargingStation>> getNearestChargingStations(@RequestParam Double latitude, @RequestParam Double longitude) {
List<ChargingStation> nearestChargingStations = openChargeMapService.getNearestChargingStations(latitude, longitude);
return new ResponseEntity<>(nearestChargingStations, HttpStatus.OK);
}
}
服务:
package com.example.assignment.chargingStations.services;
import com.example.assignment.chargingStations.clients.OpenChargeMapClient;
import com.example.assignment.chargingStations.models.ChargingStation;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
import java.util.List;
@Service
public class OpenChargeMapService {
private static final Double MAX_LATITUDE = 90.0000000;
private static final Double MIN_LATITUDE = -90.0000000;
private static final Double MAX_LONGITUDE = 180.0000000;
private static final Double MIN_LONGITUDE = -180.0000000;
private final OpenChargeMapClient openChargeMapClient;
public OpenChargeMapService(OpenChargeMapClient openChargeMapClient) {
this.openChargeMapClient = openChargeMapClient;
}
public List<ChargingStation> getNearestChargingStations(Double latitude, Double longitude) {
validateLatitudeLongitude(latitude, longitude);
List<ChargingStation> chargingStationsList = openChargeMapClient.getNearestChargingStations(latitude, longitude);
return chargingStationsList;
}
private void validateLatitudeLongitude(Double latitude, Double longitude) {
if (isLatitudeOutOfRange(latitude) || isLongitudeOutOfRange(longitude)) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Values for latitude and longitude are out of range.");
}
}
private boolean isLatitudeOutOfRange(Double latitude) {
return (latitude.compareTo(MIN_LATITUDE) < 0) || (latitude.compareTo(MAX_LATITUDE) > 0);
}
private boolean isLongitudeOutOfRange(Double longitude) {
return (longitude.compareTo(MIN_LONGITUDE) < 0) || (longitude.compareTo(MAX_LONGITUDE) > 0);
}
}
build.gradle:
plugins {
id 'org.springframework.boot' version '2.5.1'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.projectlombok:lombok:1.18.18'
implementation 'junit:junit:4.13.1'
annotationProcessor 'org.projectlombok:lombok:1.18.18'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
因为您使用的是 Spring Boot.你有两个选择。您可以单独测试 class,也可以在 spring 上下文中加载。
这两种方法都有其优点和局限性。
将模拟直接提供给测试中的 class。
public class Test {
ClassToMock service = mock(ClassToMock.class)
ClassInTest testService = new ClassInTest(service);
@Test
public void doTest() {
....
}
}
通过这种方法,您可以直接向测试中的服务提供模拟。这将导致更快的运行时间,但您将无法进行更全面的集成测试。
将模拟服务直接加载到 spring 上下文中
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class Test {
@MockBean
ClassToMock service;
@Autowired
ClassInTest testService;
@Test
public void doTest() {
....
}
}
这将生成 spring 上下文并模拟您创建的服务组件。这样做会测试 bean 的连接和测试中的服务。如果您想模拟封装服务(即存储库 layer/data 层),您还将拥有更大的灵活性。您还应该能够将 @Mock 和 @InjectMock 与您当前正在使用的 class 注释一起使用。
我正在使用“RunWith(MockitoJUnitRunner.class)”、@Mock notationt 来模拟服务,并使用@InjectMocks notation 将模拟服务注入控制器。我在“何时”的 ChargingStationsControllerTest:40 中得到 NullPointerException。我调试并意识到模拟是空的。 这是我的代码:
package com.example.assignmentchargingstations.controllers;
import com.example.assignment.chargingStations.controllers.ChargingStationsController;
import com.example.assignment.chargingStations.models.AddressInfo;
import com.example.assignment.chargingStations.models.ChargingStation;
import com.example.assignment.chargingStations.models.StatusType;
import com.example.assignment.chargingStations.services.OpenChargeMapService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import java.util.ArrayList;
import java.util.List;
import static org.mockito.ArgumentMatchers.anyDouble;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class ChargingStationsControllerTest {
@Mock
private OpenChargeMapService openChargeMapService;
@InjectMocks
private ChargingStationsController chargingStationsController;
@Test
public void testGetNearestChargingStations() throws Exception {
Double latitude = 90.0;
Double longitude = 90.0;
List<ChargingStation> chargingStationsListExpected = new ArrayList<>();
StatusType statusTypeExpected = StatusType.builder().IsOperational(true).build();
AddressInfo addressInfoExpected = AddressInfo.builder().Latitude(latitude).Longitude(longitude).Title("Test Charging Station").build();
chargingStationsListExpected.add(ChargingStation.builder().StatusType(statusTypeExpected).AddressInfo(addressInfoExpected).build());
when(openChargeMapService.getNearestChargingStations(anyDouble(), anyDouble())).thenReturn(chargingStationsListExpected);
ResponseEntity<List<ChargingStation>> responseExpected = chargingStationsController.getNearestChargingStations(latitude, longitude);
Assertions.assertEquals(responseExpected.getStatusCode(), HttpStatus.OK);
Assertions.assertEquals(responseExpected.getBody(), chargingStationsListExpected);
}
}
我正在测试的控制器:
package com.example.assignment.chargingStations.controllers;
import com.example.assignment.chargingStations.models.ChargingStation;
import com.example.assignment.chargingStations.services.OpenChargeMapService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@Controller
public class ChargingStationsController {
private final OpenChargeMapService openChargeMapService;
public ChargingStationsController(OpenChargeMapService openChargeMapService) {
this.openChargeMapService = openChargeMapService;
}
@RequestMapping(path = "/nearest-charging-stations", method = RequestMethod.GET)
public ResponseEntity<List<ChargingStation>> getNearestChargingStations(@RequestParam Double latitude, @RequestParam Double longitude) {
List<ChargingStation> nearestChargingStations = openChargeMapService.getNearestChargingStations(latitude, longitude);
return new ResponseEntity<>(nearestChargingStations, HttpStatus.OK);
}
}
服务:
package com.example.assignment.chargingStations.services;
import com.example.assignment.chargingStations.clients.OpenChargeMapClient;
import com.example.assignment.chargingStations.models.ChargingStation;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
import java.util.List;
@Service
public class OpenChargeMapService {
private static final Double MAX_LATITUDE = 90.0000000;
private static final Double MIN_LATITUDE = -90.0000000;
private static final Double MAX_LONGITUDE = 180.0000000;
private static final Double MIN_LONGITUDE = -180.0000000;
private final OpenChargeMapClient openChargeMapClient;
public OpenChargeMapService(OpenChargeMapClient openChargeMapClient) {
this.openChargeMapClient = openChargeMapClient;
}
public List<ChargingStation> getNearestChargingStations(Double latitude, Double longitude) {
validateLatitudeLongitude(latitude, longitude);
List<ChargingStation> chargingStationsList = openChargeMapClient.getNearestChargingStations(latitude, longitude);
return chargingStationsList;
}
private void validateLatitudeLongitude(Double latitude, Double longitude) {
if (isLatitudeOutOfRange(latitude) || isLongitudeOutOfRange(longitude)) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Values for latitude and longitude are out of range.");
}
}
private boolean isLatitudeOutOfRange(Double latitude) {
return (latitude.compareTo(MIN_LATITUDE) < 0) || (latitude.compareTo(MAX_LATITUDE) > 0);
}
private boolean isLongitudeOutOfRange(Double longitude) {
return (longitude.compareTo(MIN_LONGITUDE) < 0) || (longitude.compareTo(MAX_LONGITUDE) > 0);
}
}
build.gradle:
plugins {
id 'org.springframework.boot' version '2.5.1'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.projectlombok:lombok:1.18.18'
implementation 'junit:junit:4.13.1'
annotationProcessor 'org.projectlombok:lombok:1.18.18'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
因为您使用的是 Spring Boot.你有两个选择。您可以单独测试 class,也可以在 spring 上下文中加载。
这两种方法都有其优点和局限性。
将模拟直接提供给测试中的 class。
public class Test {
ClassToMock service = mock(ClassToMock.class)
ClassInTest testService = new ClassInTest(service);
@Test
public void doTest() {
....
}
}
通过这种方法,您可以直接向测试中的服务提供模拟。这将导致更快的运行时间,但您将无法进行更全面的集成测试。
将模拟服务直接加载到 spring 上下文中
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class Test {
@MockBean
ClassToMock service;
@Autowired
ClassInTest testService;
@Test
public void doTest() {
....
}
}
这将生成 spring 上下文并模拟您创建的服务组件。这样做会测试 bean 的连接和测试中的服务。如果您想模拟封装服务(即存储库 layer/data 层),您还将拥有更大的灵活性。您还应该能够将 @Mock 和 @InjectMock 与您当前正在使用的 class 注释一起使用。