我正在为我的Spring启动应用程序Rest控制器编写集成测试。
当我使用@Transactional
注释测试类时,它不能按预期工作,当我删除注释时,它会正常传递。
在测试类上使用@Transactional
是否绝对意味着
什么都没有写入数据库?我的其他测试工作正常!
他们或多或少地做同样的工作。他们写/更新/读,但这
test测试删除端点。
如果使用@Transactional注释测试类意味着无法控制数据持久性,为什么人们甚至会在测试中使用它?我将实体经理注入测试类并调用了flush
和clear
,但没有帮助。
即使数据没有写入数据库,它们仍然存在,对吧?不调用repository.delete
是否应该从持久化上下文中删除该项?
不影响db(delete)的代码位于Service层。它是在Controller中调用的,我正在测试,而不是测试类。无论测试类是否使用@Transacational
进行注释,我都希望它能够正常工作。
注意服务层为@Transactional
这是在服务层中,由控制器调用。它不是在测试中称为形式。
public void delete(long groupId, String username) {
Group group = this.loadById(groupId);
User user = userService.loadByUsername(username);
groupRepository.delete(groupId);
}
修改1
失败的测试代码:
/*
* Deleting a group shouldn't delete the members of that group
*/
@Test
public void testDeleteGroupWithMembers() throws Exception {
Principal mockPrincipal = Mockito.mock(Principal.class);
Mockito.when(mockPrincipal.getName()).thenReturn(DUMMY_USERNAME);
User admin = userTestingUtil.createUser(DUMMY_USERNAME, DUMMY_USER_NAME, null, null);
Group group = groupTestingUtil.createGroup(DUMMY_GROUP_NAME, DUMMY_GROUP_DESCRIPTION, DUMMY_IMAGE_ID, admin);
User member = userTestingUtil.createUser("test1@test.test", "testUser1" , null, null);
group.addMember(member);
RequestBuilder requestBuilder = MockMvcRequestBuilders
.delete(GROUP_ENDPOINT_URL + group.getId())
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.principal(mockPrincipal);
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response = result.getResponse();
int status = response.getStatus();
String content = response.getContentAsString();
Assert.assertEquals("wrong response status", 200, status);
Assert.assertEquals("wrong response content", "", content);
//This test fails, as the group is not yet deleted from the repo
Assert.assertEquals("there should be no group left", 0, Lists.newArrayList(groupRepository.findAll()).size());
Assert.assertEquals("wrong number of users exist", 2, Lists.newArrayList(userRepository.findAll()).size());
Assert.assertTrue("admin shouldn't get deleted when deleting a group", userRepository.findById(admin.getId()) != null);
Assert.assertTrue("group members shouldn't get deleted when deleting a group", userRepository.findById(member.getId()) != null);
}
在同一测试类中运行的测试代码:
@Test
public void testCreateGroup() throws Exception {
Principal mockPrincipal = Mockito.mock(Principal.class);
Mockito.when(mockPrincipal.getName()).thenReturn(DUMMY_USERNAME);
User user = userTestingUtil.createUser(DUMMY_USERNAME, DUMMY_USER_NAME, null, null);
JSONObject jo = new JSONObject();
jo.put(NAME_FIELD_NAME, DUMMY_GROUP_NAME);
jo.put(DESCRIPTION_FIELD_NAME, DUMMY_GROUP_DESCRIPTION);
jo.put(IMAGE_FIELD_NAME, DUMMY_IMAGE);
String testGroupJson = jo.toString();
RequestBuilder requestBuilder = MockMvcRequestBuilders
.post(GROUP_ENDPOINT_URL).content(testGroupJson)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.principal(mockPrincipal);
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response = result.getResponse();
int status = response.getStatus();
String content = response.getContentAsString();
List<Group> createdGroups = Lists.newArrayList(groupRepository.findAll());
Group createdGroup = createdGroups.get(0);
Assert.assertEquals("wrong response status", 200, status);
Assert.assertEquals("wrong response content", "", content);
Assert.assertEquals("wrong number of groups created", 1, createdGroups.size());
Assert.assertEquals("wrong group name", DUMMY_GROUP_NAME, createdGroup.getName());
Assert.assertEquals("wrong group description", DUMMY_GROUP_DESCRIPTION, createdGroup.getDescription());
Assert.assertEquals("wrong admin is assigned to the group", user.getId(), createdGroup.getAdmin().getId());
List<Group> groups = userTestingUtil.getOwnedGroups(user.getId());
Assert.assertEquals("wrong number of groups created for the admin", 1, groups.size());
Assert.assertEquals("wrong group is assigned to the admin", user.getOwnedGroups().get(0).getId(), createdGroup.getAdmin().getId());
Assert.assertTrue("image file was not created", CommonUtils.getImageFile(createdGroup.getImageId()).exists());
}
在GroupService
:
public void create(String groupName, String description, String image, String username) throws IOException {
User user = userService.loadByUsername(username);
Group group = new Group();
group.setAdmin(user);
group.setName(groupName);
group.setDescription(description);
String imageId = CommonUtils.decodeBase64AndSaveImage(image);
if (imageId != null) {
group.setImageId(imageId);
}
user.addOwnedGroup(group);
groupRepository.save(group);
logger.debug("Group with name " + group.getName() + " and id " + group.getId() + " was created");
}
public void delete(long groupId, String username) {
Group group = this.loadById(groupId);
User user = userService.loadByUsername(username);
validateAdminAccessToGroup(group, user);
groupRepository.delete(groupId);
logger.debug("Group with id " + groupId + " was deleted");
}
其余控制器的代码:
/*
* Create a group
*/
@RequestMapping(path = "", method = RequestMethod.POST)
public void create(@RequestBody PostGroupDto groupDto, Principal principal, BindingResult result) throws IOException {
createGroupDtoValidator.validate(groupDto, result);
if (result.hasErrors()) {
throw new ValidationException(result.getFieldError().getCode());
}
groupService.create(groupDto.getName(), groupDto.getDescription(), groupDto.getImage(), principal.getName());
}
/*
* Delete a group
*/
@RequestMapping(path = "/{groupId}", method = RequestMethod.DELETE)
public void delete(@PathVariable long groupId, Principal principal) {
groupService.delete(groupId, principal.getName());
}
修改2
我尝试删除User
而不是Group
,但它也不起作用。在同一方法(组服务层的delete
方法)创建组工作,但删除不!
答案 0 :(得分:6)
当使用@Transactional
注释测试时,它会回滚。
- 在测试类上使用@Transactional是否意味着什么都没有写入数据库?我的其他测试工作正常!他们或多或少都做同样的工作。
醇>
请发布您的其他测试以获取更多详细信息。
- 如果使用@Transactional注释测试类意味着无法控制数据持久性,为什么人们甚至会在测试中使用它 ?
醇>
防止用测试数据填充数据库。
- 即使数据没有写入数据库,它们仍然存在,对吧?不调用repository.delete应该从中删除该项 持久化背景?
醇>
您在哪里检查项目是否已从持久性上下文中删除?
- 不影响db(删除)的代码位于Service层。它是在控制器内召唤的,我是 测试,而不是测试类。无论如何,我都希望它能够发挥作用 事实上,测试类是用@Transacational注释的。
醇>
测试中的每个方法都包含在Spring事务中,因此在测试结束之前可能不会提交数据。
检查详细答案:
答案 1 :(得分:5)
经过一段时间的挖掘,我发现了问题所在。
User
类包含Group
个实体的列表。在delete
服务层的Group
方法中,我必须从用户指向的组列表中删除已删除的组。令人失望的是,持久化上下文不会引发任何异常。
答案 2 :(得分:0)
从更广泛的角度来看,我认为你可能正在使用错误的工具来完成工作。
测试需要彼此隔离,因此测试的顺序不会对结果产生影响。
这是在JUnit中实现的,例如为每个要执行的测试方法创建测试类的新实例。
对于在特定事务中测试逻辑的集成测试,可以通过在测试开始时启动事务然后在测试结束时将其回滚来实现。因此,数据库不会携带任何测试特定数据,因此可以在下一次测试中使用。
为了测试休息控制器,可能还需要另一种方法。您可能在该控制器内的某个位置启动事务,而不是在生产环境中运行实际的其余控制器代码之前调用它。您可能遇到控制器导致与db之外的其他系统进行通信的情况(如果在控制器测试中允许这些系统)。或者,您可能遇到在同一个静态控制器调用中执行多个事务的情况,或者使用非默认事务隔离的事务等等。这些案例不能使用@Transactional
测试用例的默认行为。
因此,您可能需要重新考虑您的测试方法并定义每组测试的测试范围。然后,基于这些范围,您可以定义如何实现隔离每个范围中的测试的策略。然后,对于每个测试运行应用适当的策略。