2017-08-23 48 views
19

我正在爲我的Spring啓動應用程序Rest控制器編寫集成測試。@Transcational測試類影響事務服務層的工作方式

當我使用@Transactional註釋測試類時,它不能按預期工作,而當我刪除註釋時,它傳遞的很好。

  1. 使用@Transactional在測試類是否意味着絕對 沒有被寫入到數據庫?我的其他測試工作正常! 他們或多或少地做同樣的工作。他們寫入/更新/讀取,但是這個測試測試了一個刪除端點。

  2. 如果使用@Transactional註釋測試類意味着無法控制數據持久性,爲什麼人們甚至在測試中使用它?我將實體經理注入了測試課程,名爲flushclear,但沒有幫助。

  3. 即使數據沒有寫入數據庫,它們仍然存在,對吧?不調用repository.delete應該從持久化上下文中刪除該項目?

  4. 不影響db(刪除)的代碼位於服務層。它是從我正在測試的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("[email protected]", "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方法)創建一個組的作品,但刪除不!

回答

5

周圍挖了一段時間後,我發現了什麼問題了。 User類有一個Group實體的列表。在Group服務層的delete方法中,我必須從用戶指向的組列表中刪除已刪除的組。令人失望的是,持久性上下文不會拋出任何異常。

+0

爲了保持謹慎,建議持久化上下文應該拋出一個異常,這表明你還沒有完全理解它是如何工作的。通常情況下,除非在調用delete方法之前事務已經開始(以及我使用的方式),否則不會從'User'中刪除'Group'。因爲如果'User'已經加載,最好保持雙向關係與數據庫同步,否則調用者將不得不調用'em.refresh(user)'來查看該組不再處於'用戶的列表(假設正確的級聯)。 –

6

當測試用@Transactional進行註釋時,它會回滾。

  1. 使用@Transactional在測試類是否意味着絕對沒有被寫入到數據庫?我的其他測試工作正常!他們或多或少地做同樣的工作。

請張貼您的其他測試的更多細節。

  • 如果註釋與@Transactional測試類意味着有數據持久性無法控制,爲什麼人們甚至用它在他們的測試中 ?
  • 防止用測試數據填充數據庫。

    1. 即使數據沒有寫入數據庫,它們仍然存在,對吧?不調用repository.delete應該從 中刪除該項目的持久化上下文?

    你在哪裏檢查項目是否從持久化上下文中刪除?

    1. 不影響db(刪除)的代碼位於Service層。這是從控制器內部調用的,我是 測試,而不是測試類。我希望它能夠工作,不管 這個事實,即測試類是用@Transacational註釋還是不註釋。

    在測試的每個方法是包裹着Spring事務,以便數據可以被不提交直到測試結束。

    檢查詳細解答:

    +0

    我知道它回滾了,但在測試完成後它會回滾。不在它的中間。否則,使用'@ Transactional'註釋的任何測試根本無法工作。 –

    +0

    我給這個問題增加了一些細節。 –

    +0

    這可能是@Transactional測試不會提交數據/命中數據庫。 – Justas

    0

    從更廣泛的角度來看,我認爲你可能會使用錯誤的工具來完成這項工作。

    測試需要彼此隔離,以便測試的順序不會對結果產生影響。

    這是在JUnit中實現的,例如爲每個要執行的測試方法創建一個新的測試類實例。

    對於在特定交易中測試邏輯的集成測試,可以通過在測試啓動時啓動事務並在測試結束時將其回滾來實現。所以數據庫不會攜帶任何測試特定數據,因此可以在下一次測試中使用。

    對於測試休息控制器可能需要另一種方法。您可能在該控制器內某處啓動事務,而不是在生產環境中運行實際的其餘控制器代碼之前調用它。您可能會遇到控制器會導致與db之外的其他系統進行通信的情況(如果控制器測試中允許這些)。或者您可能會遇到多個事務在相同的其他控制器調用中完成或使用非默認事務隔離等的事務。這些情況不適用於@Transactional測試用例的默認行爲。

    因此,您可能需要重新考慮您的測試方法並定義每組測試的測試範圍。然後基於這些範圍,您可以定義如何隔離每個範圍內的測試的策略。然後爲每個測試運行應用適當的策略。