Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
so3500 opened this issue Aug 29, 2020 · 1 comment
Assignees
Labels
enhancement New feature or request

Comments

@so3500
Copy link
Contributor

so3500 commented Aug 29, 2020

참고 링크 #24 (comment)

이 메서드를 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 
...
@so3500 so3500 added the enhancement New feature or request label Aug 29, 2020
@so3500 so3500 self-assigned this Aug 29, 2020
@so3500 so3500 mentioned this issue Aug 29, 2020
2 tasks
@raccoonback
Copy link
Collaborator

방법 1

  • @Transaction 사용
@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")));
	}
  • 문제점: shouldGetMessageList_inRoomIdWith1 테스트시 room id는 3, 4로 배정되어 message 생성 쿼리시 무결성 제약 조건 위반
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 실행

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")));
	}
  • 문제점: 각 테스트 케이스 실행시 생성된 message id를 메서드 내에서 참조할 수 없음
    즉, 각 테스트 순서(@Order())를 지정하고 /test-sql/messages.sql 에서 생성된 message id를 테스트에서 추측해야함

방법 3

  • TestEntityManager 사용
@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()));
	}

}
  • 이 방법이 가장 나은듯...?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants