Spring 启动 API 测试抛出 nullPointerException
Spring Boot API test throwing nullPointerException
所以我正在尝试测试我的 api 并且当我尝试使用存储库时它给了我一个空指针异常。
据说我的 application.yml(在 test/resources 内)有一个 h2 数据库用于测试,如下所示:
spring:
profiles:
active: test
datasource:
username:
password:
url: jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE;
platform: h2
这是我的 ControllerAPItest.java
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureTestDatabase
class ControllerAPITest {
@Mock
CompaniaRepository companiaRepository;
@Mock
DefaultGroupRepository defaultGroupRepository;
@Mock
OfficeRepository officeRepository;
@InjectMocks
CompaniaServiceImpl companiaService;
ControllerAPI controllerAPI = new ControllerAPI(companiaService);
@BeforeEach
void setUp() {
Compania compania = new Compania();
compania.setName("companiaTest");
compania.setDominio("dominioTest");
compania.setAltas("altasTest");
compania.setBajas("bajasTest");
compania.setDefault_group(null);
compania.setOffice(null);
companiaRepository.save(compania);
}
@AfterEach
void tearDown() {
companiaRepository.deleteAll();
}
@Test
void getByField() {
}
@Test
void getCompanias() {
System.out.println(controllerAPI.GetCompanias());
}
}
这是我得到的错误的输出:
java.lang.NullPointerException at
controller.ControllerAPI.GetCompanias(ControllerAPI.java:114) at
controller.ControllerAPITest.getCompanias(ControllerAPITest.java:65)
它指向的行是:
companiaRepository.save(compania);
这是完整的错误:https://textdoc.co/eGmu72wa1ydEoJ4T
我的代码出了什么问题?
谢谢!
编辑。这是我控制器中的 getCompanias。
@ResponseStatus(HttpStatus.OK)
@GetMapping()
public Map<String, List<Compania>> GetCompanias() {
Map<String, List<Compania>> mappedResult = Collections.singletonMap("result", companiaService.getCompanias());
return mappedResult;
// return companiaService.getCompanias();
}
这里使用companiaService:
public List<Compania> getCompanias() {
return companiaRepository.findAll();
}
谢谢!
测试设置有很多问题,我的建议是通读 mockito 和 spring 测试基础知识,例如 here and here .
测试设置
对于初学者来说,代码混合了不同的测试设置。注释 @RunWith(SpringRunner.class)
建议使用 JUnit4 设置,但 strack trace 指示使用 JUnit5,因为 jupiter 包位于类路径中 (org.junit.jupiter.engine.execution....
)。 @RunWith
可以删除,因为 JUnit5 依赖于扩展,并且 spring 扩展(@ExtendWith(SpringExtension.class)
)已经作为元注释存在于 @SpringBootTest
注释(source).
模拟注释的使用
当您计划创建 Spring 启动测试时,可以使用 @MockBean
注释将模拟注入到 TestApplicationContext 中。但是,通过将 @Mock
与 @InjectMocks
一起使用,模拟将仅由 Mockito 创建,而不会在应用程序上下文中注册。有关差异的更多信息,请参见 here.
模拟准备
模拟是 类 的“假”实现,用于模仿真实对象的行为。它不是一个真实的对象,它只执行您分配给它的任务。然而,在 setup() 中,该方法尝试将模拟作为真实对象访问。这不会起作用,因为它不是设置模拟的正确方法:
// The CompaniaRepository is annotated as being a mock
@Mock
CompaniaRepository companiaRepository;
...
@BeforeEach
void setUp() {
Compania compania = new Compania();
...
// Here a method on the mock is invoked, which has no use
companiaRepository.save(compania);
}
可以找到有关设置 Mockito 模拟的更多信息 here。
此外,如果您打算设置数据库测试,我不建议模拟存储库,因为它减少了使用数据库的要求(假设没有第三个存储库需要 H2)。
最后一件事; Spring 最好通过 Web 层执行测试来测试控制器。使用 new ControllerApi(...)
实例化被测控制器将只测试方法,而不是它们映射到的端点。可以找到有关如何测试 Web 层的精彩教程 here。
所以我正在尝试测试我的 api 并且当我尝试使用存储库时它给了我一个空指针异常。
据说我的 application.yml(在 test/resources 内)有一个 h2 数据库用于测试,如下所示:
spring:
profiles:
active: test
datasource:
username:
password:
url: jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE;
platform: h2
这是我的 ControllerAPItest.java
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureTestDatabase
class ControllerAPITest {
@Mock
CompaniaRepository companiaRepository;
@Mock
DefaultGroupRepository defaultGroupRepository;
@Mock
OfficeRepository officeRepository;
@InjectMocks
CompaniaServiceImpl companiaService;
ControllerAPI controllerAPI = new ControllerAPI(companiaService);
@BeforeEach
void setUp() {
Compania compania = new Compania();
compania.setName("companiaTest");
compania.setDominio("dominioTest");
compania.setAltas("altasTest");
compania.setBajas("bajasTest");
compania.setDefault_group(null);
compania.setOffice(null);
companiaRepository.save(compania);
}
@AfterEach
void tearDown() {
companiaRepository.deleteAll();
}
@Test
void getByField() {
}
@Test
void getCompanias() {
System.out.println(controllerAPI.GetCompanias());
}
}
这是我得到的错误的输出:
java.lang.NullPointerException at controller.ControllerAPI.GetCompanias(ControllerAPI.java:114) at controller.ControllerAPITest.getCompanias(ControllerAPITest.java:65)
它指向的行是:
companiaRepository.save(compania);
这是完整的错误:https://textdoc.co/eGmu72wa1ydEoJ4T
我的代码出了什么问题?
谢谢!
编辑。这是我控制器中的 getCompanias。
@ResponseStatus(HttpStatus.OK)
@GetMapping()
public Map<String, List<Compania>> GetCompanias() {
Map<String, List<Compania>> mappedResult = Collections.singletonMap("result", companiaService.getCompanias());
return mappedResult;
// return companiaService.getCompanias();
}
这里使用companiaService:
public List<Compania> getCompanias() {
return companiaRepository.findAll();
}
谢谢!
测试设置有很多问题,我的建议是通读 mockito 和 spring 测试基础知识,例如 here and here .
测试设置
对于初学者来说,代码混合了不同的测试设置。注释 @RunWith(SpringRunner.class)
建议使用 JUnit4 设置,但 strack trace 指示使用 JUnit5,因为 jupiter 包位于类路径中 (org.junit.jupiter.engine.execution....
)。 @RunWith
可以删除,因为 JUnit5 依赖于扩展,并且 spring 扩展(@ExtendWith(SpringExtension.class)
)已经作为元注释存在于 @SpringBootTest
注释(source).
模拟注释的使用
当您计划创建 Spring 启动测试时,可以使用 @MockBean
注释将模拟注入到 TestApplicationContext 中。但是,通过将 @Mock
与 @InjectMocks
一起使用,模拟将仅由 Mockito 创建,而不会在应用程序上下文中注册。有关差异的更多信息,请参见 here.
模拟准备
模拟是 类 的“假”实现,用于模仿真实对象的行为。它不是一个真实的对象,它只执行您分配给它的任务。然而,在 setup() 中,该方法尝试将模拟作为真实对象访问。这不会起作用,因为它不是设置模拟的正确方法:
// The CompaniaRepository is annotated as being a mock
@Mock
CompaniaRepository companiaRepository;
...
@BeforeEach
void setUp() {
Compania compania = new Compania();
...
// Here a method on the mock is invoked, which has no use
companiaRepository.save(compania);
}
可以找到有关设置 Mockito 模拟的更多信息 here。
此外,如果您打算设置数据库测试,我不建议模拟存储库,因为它减少了使用数据库的要求(假设没有第三个存储库需要 H2)。
最后一件事; Spring 最好通过 Web 层执行测试来测试控制器。使用 new ControllerApi(...)
实例化被测控制器将只测试方法,而不是它们映射到的端点。可以找到有关如何测试 Web 层的精彩教程 here。