编写测试 Spring 启动 REST API 从数据库检索数据
Writing a test to Spring boot REST API that retrieve data from a DB
我有一个 spring 引导 REST API 和一个 returns 数据在数据库中可用的 GET 方法。我正在尝试编写一个集成测试来测试这个 API 方法。我已将测试配置为使用 H2 数据库。我试图在执行测试之前向数据库添加一些模拟数据,并查看 API 是否检索到该数据。以下是我到目前为止编写的代码。
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
@TestPropertySource(locations = "classpath:application-test.properties")
public class MetaControllerTest {
@Autowired
private MockMvc mvc;
@Autowired
private ProvinceDAO provinceDAO;
@Transactional
@Before
public void addData () {
Province southern = getProvinceEntity("Southern", "දකුණ", "தென்");
provinceDAO.createEntity(southern);
System.out.println(provinceDAO.findAll(Province.class).size());
}
@Test
public void testGetProvinces() throws Exception {
MvcResult result = mvc.perform(get("/meta/provinces"))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andReturn();
System.out.println(result.getResponse().getContentAsString());
}
}
但是,当我 运行 这段代码时,我收到一条错误消息“org.springframework.dao.InvalidDataAccessApiUsageException: No transactional EntityManager available; nested exception is java.lang.IllegalStateException: No transactional EntityManager available
”
我也曾尝试使用 @MockBean
而不是 @Autowired
来绑定 provinceDAO
。尽管这可以防止错误,但它不会将实体保存在数据库中。
我应该如何编写我的测试用例来测试我的方法?
更新:
应用-test.properties
spring.datasource.url = jdbc:h2:mem:test
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect
实体 -> Province.java
@Entity
@Table(name = "w4a_province")
public class Province {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private int id;
@Column(name = "province_name")
private String name;
@Column(name = "province_name_si")
private String nameSi;
@Column(name = "province_name_ta")
private String nameTa;
.
.
}
GenericDAO.java
@Repository
public class GenericDAO<T> implements IGenericDAO<T> {
@PersistenceContext
private EntityManager em;
@Override
public Session getCurrentSession() {
return this.em.unwrap(Session.class);
}
@Override
public T findByPrimaryKey(Class<T> clazz, Object primaryKey) {
return getCurrentSession().find(clazz, primaryKey);
}
@Override
public List<T> findAll(Class<T> clazz) {
DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
return criteria.getExecutableCriteria(getCurrentSession()).list();
}
@Override
public T createEntity(T entity) {
getCurrentSession().save(entity);
return entity;
}
ProvinceDAOImpl.java
@Repository
public class ProvinceDAOImpl extends GenericDAO<Province> implements ProvinceDAO {
}
MetaController.java
@RestController
@PreAuthorize("permitAll()")
public class MetaController {
private final MetaService metaService;
@Autowired
public MetaController(MetaService metService) {
this.metaService = metService;
}
@GetMapping("/meta/provinces")
public ResponseEntity<List<ProvinceDTO>> getProvinces() {
if (logger.isDebugEnabled()) {
logger.debug("Retrieving list of provinces.");
}
List<ProvinceDTO> provinces = metaService.getProvinces();
return ResponseEntity.ok(provinces);
}
}
MetaServiceImpl.java
@Service
@Transactional
public class MetaServiceImpl implements MetaService {
private final ProvinceDAO provinceDAO;
@Autowired
public MetaServiceImpl(ProvinceDAO provnceDAO) {
this.provinceDAO = provnceDAO;
}
public List<ProvinceDTO> getProvinces() {
if (logger.isDebugEnabled()) {
logger.debug("Obtaining a list of provinces from database.");
}
List<Province> entities = provinceDAO.findAll(Province.class);
if (logger.isDebugEnabled()) {
logger.debug("Converting province entities to dtos.");
}
List<ProvinceDTO> dtos = new ArrayList<>();
for (int i = 0; i < entities.size(); i++) {
Province entity = entities.get(i);
if (LocaleContextHolder.getLocale().getLanguage().equals(
GlobalConstants.LanguageIdentifiers.SINHALA_LANGUAGE_TAG)) {
dtos.add(new ProvinceDTO(entity.getId(), entity.getNameSi()));
} else if (LocaleContextHolder.getLocale().getLanguage().equals(
GlobalConstants.LanguageIdentifiers.TAMIL_LANGUAGE_TAG)) {
dtos.add(new ProvinceDTO(entity.getId(), entity.getNameTa()));
} else {
dtos.add(new ProvinceDTO(entity.getId(), entity.getName()));
}
}
return dtos;
}
}
为了测试 Rest Api 您可以尝试功能测试以及集成测试。
您可以根据需要准备自己的响应格式并检查是否相同 returned 或者您还可以验证来自数据库的数据是否正常或 not.Plz 检查下面的示例
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = FactsMain.class)
@WebAppConfiguration
public abstract class BaseTest {
protected MockMvc mvc;
@Autowired
WebApplicationContext webApplicationContext;
protected void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
protected String mapToJson(Object obj) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(obj);
}
protected <T> T mapFromJson(String json, Class<T> clazz)
throws JsonParseException, JsonMappingException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(json, clazz);
}
}
在第一个测试用例中,我正在形成响应格式并尝试 return 相同,然后验证 same.Here 我不需要数据库数据,所以我将服务保留为模拟auto wired.And 使用 ObjectMapper 将 json 转换为 java,然后将 java obj 从基本测试 class.
转换为 json
public class PersonalDetailsControllerTest extends BaseTest {
@MockBean
private IPersonalService service;
private static final String URI = "/api/personalDetails";
@Override
@Before
public void setUp() {
super.setUp();
}
@Test
public void testGet() throws Exception {
PersonalDetailsEntity entity = new PersonalDetailsEntity();
List<PersonalDetailsEntity> dataList = new ArrayList<PersonalDetailsEntity>();
FactsAdminResponse<PersonalDetailsEntity> dataResponse = new FactsAdminResponse<PersonalDetailsEntity>();
entity.setId(1);
entity.setName(“Anthony Holmes”);
entity.setAge(26);
entity.setCity(“Banglore”);
entity.setCountry(“India”);
dataList.add(entity);
dataResponse.setData(dataList);
Mockito.when(service.getBuildings()).thenReturn(dataList);
RequestBuilder requestBuilder = MockMvcRequestBuilders.get(URI)
.accept(MediaType.APPLICATION_JSON);
MvcResult mvcResult = mvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response = mvcResult.getResponse();
String expectedJson = this.mapToJson(dataResponse);
String outputInJson = mvcResult.getResponse().getContentAsString();
assertEquals(HttpStatus.OK.value(), response.getStatus());
assertEquals(expectedJson, outputInJson);
}
}
在下面的例子中,我们正在获取 json 格式的实际数据,因为我们正在进行休息 api 调用,然后除了验证状态之外,您还可以交叉检查数据
public class PersonalDetailsControllerTest extends BaseTest {
private static final String URI = "/api/personalDetails";
@Override
@Before
public void setUp() {
super.setUp();
}
@Test
public void getGet() throws Exception {
MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(URL)
.accept(MediaType.APPLICATION_JSON_VALUE)).andReturn();
int status = mvcResult.getResponse().getStatus();
assertEquals(200, status);
String content = mvcResult.getResponse().getContentAsString();
//you got the content in string format now you can also validate the data
}
我设法通过在 test/resources 文件夹中放置一个带有插入查询的 SQL 脚本数据-h2.sql 来向数据库提供所需的数据。这避免了使用 EntityManager 或 DAO 的要求。
此外,我在 application-test.properties
文件中添加了以下 属性。
spring.datasource.platform=h2
我有一个 spring 引导 REST API 和一个 returns 数据在数据库中可用的 GET 方法。我正在尝试编写一个集成测试来测试这个 API 方法。我已将测试配置为使用 H2 数据库。我试图在执行测试之前向数据库添加一些模拟数据,并查看 API 是否检索到该数据。以下是我到目前为止编写的代码。
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
@TestPropertySource(locations = "classpath:application-test.properties")
public class MetaControllerTest {
@Autowired
private MockMvc mvc;
@Autowired
private ProvinceDAO provinceDAO;
@Transactional
@Before
public void addData () {
Province southern = getProvinceEntity("Southern", "දකුණ", "தென்");
provinceDAO.createEntity(southern);
System.out.println(provinceDAO.findAll(Province.class).size());
}
@Test
public void testGetProvinces() throws Exception {
MvcResult result = mvc.perform(get("/meta/provinces"))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andReturn();
System.out.println(result.getResponse().getContentAsString());
}
}
但是,当我 运行 这段代码时,我收到一条错误消息“org.springframework.dao.InvalidDataAccessApiUsageException: No transactional EntityManager available; nested exception is java.lang.IllegalStateException: No transactional EntityManager available
”
我也曾尝试使用 @MockBean
而不是 @Autowired
来绑定 provinceDAO
。尽管这可以防止错误,但它不会将实体保存在数据库中。
我应该如何编写我的测试用例来测试我的方法?
更新:
应用-test.properties
spring.datasource.url = jdbc:h2:mem:test
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect
实体 -> Province.java
@Entity
@Table(name = "w4a_province")
public class Province {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private int id;
@Column(name = "province_name")
private String name;
@Column(name = "province_name_si")
private String nameSi;
@Column(name = "province_name_ta")
private String nameTa;
.
.
}
GenericDAO.java
@Repository
public class GenericDAO<T> implements IGenericDAO<T> {
@PersistenceContext
private EntityManager em;
@Override
public Session getCurrentSession() {
return this.em.unwrap(Session.class);
}
@Override
public T findByPrimaryKey(Class<T> clazz, Object primaryKey) {
return getCurrentSession().find(clazz, primaryKey);
}
@Override
public List<T> findAll(Class<T> clazz) {
DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
return criteria.getExecutableCriteria(getCurrentSession()).list();
}
@Override
public T createEntity(T entity) {
getCurrentSession().save(entity);
return entity;
}
ProvinceDAOImpl.java
@Repository
public class ProvinceDAOImpl extends GenericDAO<Province> implements ProvinceDAO {
}
MetaController.java
@RestController
@PreAuthorize("permitAll()")
public class MetaController {
private final MetaService metaService;
@Autowired
public MetaController(MetaService metService) {
this.metaService = metService;
}
@GetMapping("/meta/provinces")
public ResponseEntity<List<ProvinceDTO>> getProvinces() {
if (logger.isDebugEnabled()) {
logger.debug("Retrieving list of provinces.");
}
List<ProvinceDTO> provinces = metaService.getProvinces();
return ResponseEntity.ok(provinces);
}
}
MetaServiceImpl.java
@Service
@Transactional
public class MetaServiceImpl implements MetaService {
private final ProvinceDAO provinceDAO;
@Autowired
public MetaServiceImpl(ProvinceDAO provnceDAO) {
this.provinceDAO = provnceDAO;
}
public List<ProvinceDTO> getProvinces() {
if (logger.isDebugEnabled()) {
logger.debug("Obtaining a list of provinces from database.");
}
List<Province> entities = provinceDAO.findAll(Province.class);
if (logger.isDebugEnabled()) {
logger.debug("Converting province entities to dtos.");
}
List<ProvinceDTO> dtos = new ArrayList<>();
for (int i = 0; i < entities.size(); i++) {
Province entity = entities.get(i);
if (LocaleContextHolder.getLocale().getLanguage().equals(
GlobalConstants.LanguageIdentifiers.SINHALA_LANGUAGE_TAG)) {
dtos.add(new ProvinceDTO(entity.getId(), entity.getNameSi()));
} else if (LocaleContextHolder.getLocale().getLanguage().equals(
GlobalConstants.LanguageIdentifiers.TAMIL_LANGUAGE_TAG)) {
dtos.add(new ProvinceDTO(entity.getId(), entity.getNameTa()));
} else {
dtos.add(new ProvinceDTO(entity.getId(), entity.getName()));
}
}
return dtos;
}
}
为了测试 Rest Api 您可以尝试功能测试以及集成测试。 您可以根据需要准备自己的响应格式并检查是否相同 returned 或者您还可以验证来自数据库的数据是否正常或 not.Plz 检查下面的示例
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = FactsMain.class)
@WebAppConfiguration
public abstract class BaseTest {
protected MockMvc mvc;
@Autowired
WebApplicationContext webApplicationContext;
protected void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
protected String mapToJson(Object obj) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(obj);
}
protected <T> T mapFromJson(String json, Class<T> clazz)
throws JsonParseException, JsonMappingException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(json, clazz);
}
}
在第一个测试用例中,我正在形成响应格式并尝试 return 相同,然后验证 same.Here 我不需要数据库数据,所以我将服务保留为模拟auto wired.And 使用 ObjectMapper 将 json 转换为 java,然后将 java obj 从基本测试 class.
转换为 jsonpublic class PersonalDetailsControllerTest extends BaseTest {
@MockBean
private IPersonalService service;
private static final String URI = "/api/personalDetails";
@Override
@Before
public void setUp() {
super.setUp();
}
@Test
public void testGet() throws Exception {
PersonalDetailsEntity entity = new PersonalDetailsEntity();
List<PersonalDetailsEntity> dataList = new ArrayList<PersonalDetailsEntity>();
FactsAdminResponse<PersonalDetailsEntity> dataResponse = new FactsAdminResponse<PersonalDetailsEntity>();
entity.setId(1);
entity.setName(“Anthony Holmes”);
entity.setAge(26);
entity.setCity(“Banglore”);
entity.setCountry(“India”);
dataList.add(entity);
dataResponse.setData(dataList);
Mockito.when(service.getBuildings()).thenReturn(dataList);
RequestBuilder requestBuilder = MockMvcRequestBuilders.get(URI)
.accept(MediaType.APPLICATION_JSON);
MvcResult mvcResult = mvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response = mvcResult.getResponse();
String expectedJson = this.mapToJson(dataResponse);
String outputInJson = mvcResult.getResponse().getContentAsString();
assertEquals(HttpStatus.OK.value(), response.getStatus());
assertEquals(expectedJson, outputInJson);
}
}
在下面的例子中,我们正在获取 json 格式的实际数据,因为我们正在进行休息 api 调用,然后除了验证状态之外,您还可以交叉检查数据
public class PersonalDetailsControllerTest extends BaseTest {
private static final String URI = "/api/personalDetails";
@Override
@Before
public void setUp() {
super.setUp();
}
@Test
public void getGet() throws Exception {
MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(URL)
.accept(MediaType.APPLICATION_JSON_VALUE)).andReturn();
int status = mvcResult.getResponse().getStatus();
assertEquals(200, status);
String content = mvcResult.getResponse().getContentAsString();
//you got the content in string format now you can also validate the data
}
我设法通过在 test/resources 文件夹中放置一个带有插入查询的 SQL 脚本数据-h2.sql 来向数据库提供所需的数据。这避免了使用 EntityManager 或 DAO 的要求。
此外,我在 application-test.properties
文件中添加了以下 属性。
spring.datasource.platform=h2