LandvibeDev / web-chat-backend

Web Chat Application 🛩
1 stars 3 forks source link

메시지 전송 api 통합 테스트 메서드를 `RoomControllerIntegrationTest` 처리할 수 있을지 고민 #25

Open so3500 opened 4 years ago

so3500 commented 4 years ago

참고 링크 https://github.com/LandvibeDev/web-chat-backend/pull/24#discussion_r479663988

이 메서드를 MessageCreationTest 에 추가한 이유

이 테스트를 RoomControllerIntegrationTest 에 추가할 때 RoomControllerIntegrationTest#shouldGetMessageList_inRoomIdWith1 가 영향 받음

하나의 클래스에서 처리할 수 없을지 고민 필요

response

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json;charset=UTF-8"]
     Content type = application/json
             Body = [{"id":1,"contents":"foo","createdAt":[2020,8,1,0,0],"messageType":"TEXT"},{"id":3,"contents":"hi hello","createdAt":[2020,8,30,1,5,8,114499000],"messageType":"TEXT"},{"id":4,"contents":"foo","createdAt":[2020,8,1,0,0],"messageType":"TEXT"}]
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

error log

JSON path "$.*"
Expected: a collection with size <1>
     but: collection size was <3>
java.lang.AssertionError: JSON path "$.*"
Expected: a collection with size <1>
     but: collection size was <3>
    at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
    at org.springframework.test.util.JsonPathExpectationsHelper.assertValue(JsonPathExpectationsHelper.java:73)
    at org.springframework.test.web.servlet.result.JsonPathResultMatchers.lambda$value$0(JsonPathResultMatchers.java:87)
    at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:196)
    at web.chat.backend.controller.RoomControllerIntegrationTest.shouldGetMessageList_inRoomIdWith1(RoomControllerIntegrationTest.java:58)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at 
...
raccoonback commented 4 years ago

방법 1

@Test
    @Sql(scripts = "/test-sql/messages.sql")
    @Transactional
    void createMessage() throws Exception {
        // given
        MessageRequest req = new MessageRequest();
        req.setContents("hi hello");

        final String body = objectMapper.writeValueAsString(req);

        // when
        ResultActions action = mockMvc.perform(
            post("/api/rooms/{roomId}/messages", 1)
                .contentType(MediaType.APPLICATION_JSON)
                .content(body));

        // then
        action.andDo(print())
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.contents").value(req.getContents()));
    }

    @Test
    @Sql(scripts = "/test-sql/messages.sql")
    @DisplayName("ID가 1인 채팅방의 메시지 리스트가 조회되야만 한다")
    @Transactional
    void shouldGetMessageList_inRoomIdWith1() throws Exception {
        mockMvc.perform(get("/api/rooms/{id}/messages", 1))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.*", not(empty())))
            .andExpect(jsonPath("$.*", hasSize(1)))
            .andExpect(jsonPath("$[0].id", is(1)))
            .andExpect(jsonPath("$[0].contents", is("foo")))
            .andExpect(jsonPath("$[0].messageType", is("TEXT")));
    }
org.springframework.jdbc.datasource.init.ScriptStatementFailedException: 
Failed to execute SQL script statement #3 of class path resource [test-sql/messages.sql]: 
INSERT INTO message(contents, message_type, created_at, room_id) VALUES ('foo', 'TEXT', '2020-08-01 00:00:00', 1); 
nested exception is org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation: "FKL1KG5A2471CV6PKEW0GDGJRMO: PUBLIC.MESSAGE FOREIGN KEY(ROOM_ID) REFERENCES PUBLIC.ROOM(ID) (1)"; 
SQL statement: INSERT INTO message(contents, message_type, created_at, room_id) VALUES ('foo', 'TEXT', '2020-08-01 00:00:00', 1) [23506-196]

방법 2

clean-up-messages.sql

delete from message;
@Test
    @Sql(scripts = "/test-sql/messages.sql")
    @Sql(scripts = "/test-sql/clean-up-messages.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    void createMessage() throws Exception {
        // given
        MessageRequest req = new MessageRequest();
        req.setContents("hi hello");

        final String body = objectMapper.writeValueAsString(req);

        // when
        ResultActions action = mockMvc.perform(
            post("/api/rooms/{roomId}/messages", 1)
                .contentType(MediaType.APPLICATION_JSON)
                .content(body));

        // then
        action.andDo(print())
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.contents").value(req.getContents()));
    }

    @Test
    @Sql(scripts = "/test-sql/messages.sql")
    @Sql(scripts = "/test-sql/clean-up-messages.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
    @DisplayName("ID가 1인 채팅방의 메시지 리스트가 조회되야만 한다")
    void shouldGetMessageList_inRoomIdWith1() throws Exception {
        mockMvc.perform(get("/api/rooms/{id}/messages", 1))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.*", not(empty())))
            .andExpect(jsonPath("$.*", hasSize(1)))
            .andExpect(jsonPath("$[0].id", is(1)))
            .andExpect(jsonPath("$[0].contents", is("foo")))
            .andExpect(jsonPath("$[0].messageType", is("TEXT")));
    }

방법 3

@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
@RequiredArgsConstructor
@SpringBootTest()
@Transactional
@AutoConfigureTestEntityManager
class RoomControllerIntegrationTest {

        // ...
        final TestEntityManager entityManager;

       @Test
    void createMessage() throws Exception {
        // given
        Room room = new Room();
        room.setTitle("foo");
        Room savedRoom = entityManager.persist(room);

        MessageRequest req = new MessageRequest();
        req.setContents("hi hello");

        final String body = objectMapper.writeValueAsString(req);

        // when
        ResultActions action = mockMvc.perform(
            post("/api/rooms/{roomId}/messages", savedRoom.getId())
                .contentType(MediaType.APPLICATION_JSON)
                .content(body));

        // then
        action.andDo(print())
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.contents").value(req.getContents()));
    }

    @Test
    @DisplayName("ID가 1인 채팅방의 메시지 리스트가 조회되야만 한다")
    void shouldGetMessageList_inRoomIdWith1() throws Exception {
                // given
        Room fooRoom = new Room();
        fooRoom.setTitle("foo");
        Room savedFooRoom = entityManager.persist(fooRoom);

        Room barRoom = new Room();
        barRoom.setTitle("bar");

        entityManager.persist(barRoom);

        List<Message> messages = Stream.of(1, 2, 3)
            .map((id) -> {
                Message message = new Message();
                message.setContents("contents_" + id);
                message.setMessageType(MessageType.TEXT);
                message.setRoom(id % 2 == 0 ? savedFooRoom : barRoom);
                return entityManager.persist(message);
            }).collect(Collectors.toList());

                // when, then
        mockMvc.perform(get("/api/rooms/{id}/messages", fooRoom.getId()))
            .andDo(print())
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.*", not(empty())))
            .andExpect(jsonPath("$.*", hasSize(1)))
            .andExpect(jsonPath("$[0].id").value(messages.get(1).getId()))
            .andExpect(jsonPath("$[0].contents").value(messages.get(1).getContents()))
            .andExpect(jsonPath("$[0].messageType").value(messages.get(1).getMessageType().name()));
    }

}