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

时间:2017-08-23 05:14:09

标签: java hibernate

我正在为我的Spring启动应用程序Rest控制器编写集成测试。

当我使用@Transactional注释测试类时,它不能按预期工作,当我删除注释时,它会正常传递。

  1. 在测试类上使用@Transactional是否绝对意味着 什么都没有写入数据库?我的其他测试工作正常! 他们或多或少地做同样的工作。他们写/更新/读,但这 test测试删除端点。

  2. 如果使用@Transactional注释测试类意味着无法控制数据持久性,为什么人们甚至会在测试中使用它?我将实体经理注入测试类并调用了flushclear,但没有帮助。

  3. 即使数据没有写入数据库,它们仍然存在,对吧?不调用repository.delete是否应该从持久化上下文中删除该项?

  4. 不影响db(delete)的代码位于Service层。它是在Controller中调用的,我正在测试,而不是测试类。无论测试类是否使用@Transacational进行注释,我都希望它能够正常工作。

  5. 注意服务层为@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方法)创建组工作,但删除不!

3 个答案:

答案 0 :(得分:6)

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

  
      
  1. 在测试类上使用@Transactional是否意味着什么都没有写入数据库?我的其他测试工作正常!他们或多或少都做同样的工作。
  2.   

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

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

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

  
      
  1. 即使数据没有写入数据库,它们仍然存在,对吧?不调用repository.delete应该从中删除该项   持久化背景?
  2.   

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

  
      
  1. 不影响db(删除)的代码位于Service层。它是在控制器内召唤的,我是   测试,而不是测试类。无论如何,我都希望它能够发挥作用   事实上,测试类是用@Transacational注释的。
  2.   

测试中的每个方法都包含在Spring事务中,因此在测试结束之前可能不会提交数据。

检查详细答案:

答案 1 :(得分:5)

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

答案 2 :(得分:0)

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

测试需要彼此隔离,因此测试的顺序不会对结果产生影响。

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

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

为了测试休息控制器,可能还需要另一种方法。您可能在该控制器内的某个位置启动事务,而不是在生产环境中运行实际的其余控制器代码之前调用它。您可能遇到控制器导致与db之外的其他系统进行通信的情况(如果在控制器测试中允许这些系统)。或者,您可能遇到在同一个静态控制器调用中执行多个事务的情况,或者使用非默认事务隔离的事务等等。这些案例不能使用@Transactional测试用例的默认行为。

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