首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >@Transcational测试类会影响事务性服务层的工作方式

@Transcational测试类会影响事务性服务层的工作方式
EN

Stack Overflow用户
提问于 2017-08-23 13:14:09
回答 3查看 1.2K关注 0票数 20

我正在为我的Spring boot app Rest控制器编写集成测试。

当我用@Transactional注释测试类时,它不会像预期的那样工作,当我删除注释时,它可以正常通过。

  1. 在测试类上使用@Transactional是否意味着绝对不会向数据库中写入任何内容?我的其他测试运行得很好!他们或多或少做着同样的工作。它们写/更新/读,但这个测试测试一个删除端点。
  2. 如果使用@Transactional注释测试类意味着无法控制数据持久性,那么人们为什么还要在测试中使用它呢?我将实体管理器注入到测试类中,并调用了flushclear,它没有帮助。
  3. 即使数据没有写入数据库,它们也是持久化的,对吧?调用repository.delete不应该从持久化上下文中删除该项吗?
  4. 不影响数据库(删除)的代码位于服务层。它是从我正在测试的控制器中调用的,而不是从测试类中调用的。无论测试类是否使用@Transacational注释,我都希望它能正常工作。

注意:服务层为@Transactional

这是在服务层中,由控制器调用。它在测试中不叫form。

代码语言:javascript
复制
public void delete(long groupId, String username) {
    Group group = this.loadById(groupId);
    User user = userService.loadByUsername(username);
    groupRepository.delete(groupId);
}

编辑1个

测试失败的代码如下:

代码语言:javascript
复制
/*
 * 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);
}

在同一个测试类中工作的测试代码:

代码语言:javascript
复制
@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中创建和删除方法:

代码语言:javascript
复制
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");
}

rest控制器的代码:

代码语言:javascript
复制
/*
 * 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

我尝试删除Group而不是User,但也不起作用。在相同的方法(组服务图层的delete方法)中,创建一个组可以,但删除不可以!

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-08-26 15:25:44

经过一段时间的挖掘,我发现了问题所在。User类有一个Group实体列表。在Group服务层的delete方法中,我必须从用户指向的组列表中删除已删除的组。令人失望的是,持久上下文没有为它抛出任何异常。

票数 5
EN

Stack Overflow用户

发布于 2017-08-23 16:41:21

当使用@Transactional注释测试时,它会回滚。

  1. 在测试类上使用@Transactional是否意味着绝对不会写入数据库?我的其他测试运行得很好!他们或多或少做着同样的工作。

请发布您的其他测试以了解更多详细信息。

  1. 如果使用@Transactional注释测试类意味着无法控制数据持久性,人们为什么还要在测试中使用它?

防止用测试数据填充数据库。

  1. 即使数据没有写入数据库,它们也是持久化的,对吧?调用repository.delete不应该从持久性上下文中删除该项吗?

在哪里检查项是否已从持久性上下文中删除?

  1. 不影响数据库(删除)的代码位于服务层。它是从我正在测试的控制器中调用的,而不是从测试类中调用的。无论测试类是否带有@Transacational注释,我都希望它能正常工作。

测试中的每个方法都包装了Spring事务,因此数据可能要到测试结束时才会提交。

查看详细答案:

票数 6
EN

Stack Overflow用户

发布于 2017-09-01 17:51:14

从更广泛的角度来看,我认为你可能在工作中使用了错误的工具。

测试需要相互隔离,这样测试运行的顺序才不会对结果产生影响。

这是在JUnit中实现的,例如,通过为要执行的每个测试方法创建测试类的新实例。

对于测试特定事务中的逻辑的集成测试,这可以通过在测试开始时启动事务,然后在测试结束时回滚它来实现。因此,该数据库不携带任何特定于测试的数据,因此可供下一次测试使用。

为了测试rest控制器,可能需要另一种方法。当事务在您的生产环境中运行时,您可能会在该控制器中的某个位置启动事务,而不是在调用实际的rest控制器代码之前。您可能会遇到控制器导致与数据库以外的其他系统进行通信的情况(如果控制器测试中允许这样做的话)。或者,您可能会遇到在同一rest控制器调用中完成多个事务的情况,或者使用非默认事务隔离的事务,等等。这些用例不能使用@Transactional测试用例的默认行为。

因此,您可能希望重新考虑您的测试方法,并定义每组测试的测试范围。然后,基于这些作用域,您可以定义如何在每个作用域中隔离测试的策略。然后,对于每个测试运行,应用适当的策略。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45831099

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档