-
Notifications
You must be signed in to change notification settings - Fork 89
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
3주차 테스트 작성하기 #100
base: tmxhsk99
Are you sure you want to change the base?
3주차 테스트 작성하기 #100
Changes from 3 commits
b8f5615
4f661a9
6da3a44
e1146dc
e468b67
04673fd
a0c183d
a645d9a
0586f34
1c0cc3f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
package com.codesoom.assignment.application; | ||
|
||
import com.codesoom.assignment.TaskNotFoundException; | ||
import com.codesoom.assignment.models.Task; | ||
import org.assertj.core.api.Assertions; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.context.SpringBootTest; | ||
import org.springframework.boot.test.mock.mockito.MockBean; | ||
|
||
import static org.junit.jupiter.api.Assertions.*; | ||
import static org.mockito.BDDMockito.given; | ||
|
||
@SpringBootTest | ||
class TaskServiceTest { | ||
|
||
TaskService taskService; | ||
|
||
@BeforeEach | ||
void setUp() { | ||
taskService = new TaskService(); | ||
} | ||
@Test | ||
@DisplayName("최초 할일 리스트 요청시 빈 리스트 반환 테스트") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. getTasks는 할 일이 비어있으면 빈 리스트를 반환한다. 요렇게 쓰는 것은 어떨까요? 테스트 대상과 주어진 상황 그래서 기대하는 결과를 적어보았어요 |
||
void getFirstTasksIsEmpty() { | ||
Assertions.assertThat(taskService.getTasks()).hasSize(0); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이렇게 임포트해서 assertThat만 사용할 수 있겠네요 import static org.assertj.core.api.Assertions.assertThat;
assertThat(taskService.getTasks()).hasSize(0); |
||
} | ||
|
||
@Test | ||
@DisplayName("기존에 할일이 2개 있는 경우 2개 조회 테스트") | ||
void getTasksSuccess() { | ||
// given | ||
Task task1 = new Task(); | ||
Task task2 = new Task(); | ||
|
||
taskService.createTask(task1); | ||
taskService.createTask(task2); | ||
|
||
// when & then | ||
Assertions.assertThat(taskService.getTasks()).hasSize(2); | ||
} | ||
|
||
@Test | ||
@DisplayName("할일이 존재하는 경우 상세 조회 테스트 ") | ||
void getTaskSuccess() { | ||
// given | ||
Task task = new Task(); | ||
task.setTitle("task0"); | ||
taskService.createTask(task); | ||
|
||
// when & then | ||
Assertions.assertThat(taskService.getTask(1L).getTitle()).isEqualTo("task0"); | ||
} | ||
|
||
@Test | ||
@DisplayName("할일 상세 조회시 할일이 없는 경우 테스트") | ||
void getTaskFail() { | ||
// when & then | ||
Assertions.assertThatThrownBy(() -> taskService.getTask(100L)).isInstanceOf(TaskNotFoundException.class); | ||
} | ||
|
||
@Test | ||
@DisplayName("할일을 생성후 할일이 생성되었는지 확인하는 테스트") | ||
void createTaskSuccess() { | ||
// given | ||
Task task = new Task(); | ||
task.setTitle("task0"); | ||
|
||
// when | ||
Task createdTask = taskService.createTask(task); | ||
|
||
// then | ||
Assertions.assertThat(createdTask.getTitle()).isEqualTo("task0"); | ||
} | ||
|
||
@Test | ||
@DisplayName("할일을 수정후 수정된 할일이 반환되는지 확인하는 테스트") | ||
void updateTaskSuccess() { | ||
// given | ||
Task task = new Task(); | ||
task.setTitle("task0"); | ||
Comment on lines
+84
to
+85
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 할 일을 만드는 것이 반복되고 있네요. 테스트를 위해서 필요한 데이터를 미리 만들어놓고 사용하는 것은 어떨까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 안녕하세요 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "책에서는 중복을 무조건 제거하는 것이 좋지 않을 수 있다." 라고 이야기하면서 단순하고 명료하게만 만들어준다면 테스트에서의 다소의 중복은 괜찮다고 이야기하고 있네요. 이부분에 대해서는 저도 적극 동의합니다. 효율적으로 작성된 테스트보다는 이해하기 쉬운 테스트여야 하죠. 책에도 예를들긴 했지만 이런 경우를 생각해볼 수 있을 것 같습니다. @Nested
@DisplayName("findProduct")
class Describe_findProduct {
@Nested
@DisplayName("할일이 존재하면")
class Context_when_product_exists {
@Test
@DisplayName("할 일을 반환합니다.")
void it_returns_empty_list() {
var product = productFinder.find(ID);
assertThat(product.getId()).isEqualTo(ID);
}
}
@Nested
@DisplayName("할일을 찾을 수 없으면")
class Context_when_product_does_not_exist {
@Test
@DisplayName("할 일을 찾을 수 없다는 예외를 던집니다.")
void it_returns_empty_list() {
assertThatThrownBy(() -> productFinder.find(9999L)) .isInstanceOf(ExpectedException.class)
}
}
} 하지만 그냥 할 일을 찾을 수 없다고 에러를 던지는 것보다 언제 저걸 던지는지 보여주고 싶다면, 혹은 할 일이 존재하는 경우를 보여주고 싶다면 @Nested
@DisplayName("findProduct")
class Describe_findProduct {
@Nested
@DisplayName("할일이 존재하면")
class Context_when_product_exists {
@BeforeEach
void setUp() {
productCreator.create(SOME_PRODUCT);
}
@Test
@DisplayName("할 일을 반환합니다.")
void it_returns_empty_list() {
var product = productFinder.find(SOME_PRODUCT.getId());
assertThat(product.getId()).isEqualTo(SOME_PRODUCT.getId());
}
}
@Nested
@DisplayName("할일을 찾을 수 없으면")
class Context_when_product_does_not_exist {
@BeforeEach
void setUp() {
productRemove.remove(SOME_PRODUCT.getId());
}
@Test
@DisplayName("할 일을 찾을 수 없다는 예외를 던집니다.")
void it_returns_empty_list() {
assertThatThrownBy(() -> productFinder.find(SOME_PRODUCT.getId())) .isInstanceOf(ExpectedException.class)
}
}
} 이런식으로 이전에 어떤 상황이었는지를 명시적으로 드러낼 수 있습니다. 여기서 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 말씀해 주신 대로 하면 어떤 행위를 했는지도 같은 블록에 있어서 |
||
taskService.createTask(task); | ||
|
||
Task updateTask = new Task(); | ||
updateTask.setTitle("task1"); | ||
|
||
// when | ||
Task updatedTask = taskService.updateTask(1L, updateTask); | ||
|
||
// then | ||
Assertions.assertThat(updatedTask.getTitle()).isEqualTo("task1"); | ||
} | ||
@Test | ||
@DisplayName("존재하지 않는 할일을 업데이트 할 경우 예외를 반환하는 테스트") | ||
void notExistTaskUpdateFail() { | ||
// given | ||
Task task = new Task(); | ||
task.setTitle("task0"); | ||
taskService.createTask(task); | ||
|
||
Task updateTask = new Task(); | ||
updateTask.setTitle("task1"); | ||
|
||
// when & then | ||
Assertions.assertThatThrownBy(() -> taskService.updateTask(100L, updateTask)).isInstanceOf(TaskNotFoundException.class); | ||
} | ||
|
||
@Test | ||
@DisplayName("할일을 삭제 후 삭제 되었는지 확인하는 테스트") | ||
void deleteTaskSuccess() { | ||
// given | ||
Task task = new Task(); | ||
task.setTitle("task0"); | ||
taskService.createTask(task); | ||
|
||
// when | ||
taskService.deleteTask(1L); | ||
|
||
// then | ||
Assertions.assertThat(taskService.getTasks()).hasSize(0); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋은 테스트를 작성하고 싶을 때 생각해보면 좋은 것이 하나가 있는데, 바로 어떤 변화가 일어나는지에 대해서 집중해서 테스트를 작성하는 것입니다. 그러면 테스트 대상을 사용함으로써 우리 애플리케이션에 어떤 변화가 일어나는지, 왜 테스트 대상을 사용해야 하는지를 더 드러낼 수 있습니다. @Test
@DisplayName("할일을 삭제 후 삭제 되었는지 확인하는 테스트")
void deleteTaskSuccess() {
// given
Task task = new Task();
task.setTitle("task0");
taskService.createTask(task);
int oldSize = taskService.getTasks();
// when
taskService.deleteTask(1L);
int newSize = taskService.getTasks();
// then
assertThat(newSize - oldSize).isEqualTo(-1);
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 뭔가 하면서도 약간 방향성 없이 했는데 |
||
|
||
@Test | ||
@DisplayName("존재하지 않는 할일을 삭제 할 경우 예외를 반환하는 테스트") | ||
void isNotExistTaskDeleteFail() { | ||
// given | ||
Task task = new Task(); | ||
task.setTitle("task0"); | ||
taskService.createTask(task); | ||
|
||
// when & then | ||
Assertions.assertThatThrownBy(() -> taskService.deleteTask(100L)).isInstanceOf(TaskNotFoundException.class); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
package com.codesoom.assignment.controllers; | ||
|
||
import com.codesoom.assignment.TaskNotFoundException; | ||
import com.codesoom.assignment.application.TaskService; | ||
import com.codesoom.assignment.models.Task; | ||
import org.assertj.core.api.Assertions; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; | ||
|
||
|
||
class TaskControllerTest { | ||
private TaskController taskController; | ||
|
||
@BeforeEach | ||
void setUp() { | ||
taskController = new TaskController(new TaskService()); | ||
} | ||
|
||
public Task createTask() { | ||
Task task = new Task(); | ||
task.setTitle("task"); | ||
return task; | ||
} | ||
|
||
@Test | ||
@DisplayName("최초 할일 생성 후 할일 리스트에 값이 늘어난지 테스트") | ||
void createTaskSuccessTest() { | ||
// given | ||
Task task = createTask(); | ||
int beforeSize = taskController.list().size(); | ||
// when | ||
Task insertTask = taskController.create(task); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. insertTask라는 이름은 할 일을 insert하는 메서드 처럼 보이네요. insertedTask라는 이름을 사용할 수 있을 것 같아요. 그런데 여기서 이변수가 사용되고 있진 않네요. |
||
|
||
// then | ||
Assertions.assertThat( | ||
beforeSize).isEqualTo(taskController.list().size() - 1); | ||
|
||
} | ||
|
||
@Test | ||
@DisplayName("최초 호출시 할일 리스트가 비어있는지 테스트.") | ||
void isFirstTasksIsEmpty() { | ||
Assertions.assertThat(taskController.list()).isEmpty(); | ||
} | ||
|
||
@Test | ||
@DisplayName("할일 복수 있는 경우 해당 개수에 맞게 나오는지 테스트.") | ||
void listSuccessTest() { | ||
// given | ||
Task task1 = createTask(); | ||
Task task2 = createTask(); | ||
|
||
// when | ||
taskController.create(task1); | ||
taskController.create(task2); | ||
|
||
// then | ||
Assertions.assertThat(taskController.list()).hasSize(2); | ||
} | ||
|
||
|
||
@Test | ||
@DisplayName("할일을 1개를 등록 후 해당 할일 상세 조회 테스트") | ||
void getTaskSuccess() { | ||
// given | ||
Task task1 = createTask(); | ||
|
||
// when | ||
taskController.create(task1); | ||
|
||
// then | ||
Assertions.assertThat(taskController.detail(1L).getTitle()).isEqualTo("task1"); | ||
|
||
} | ||
|
||
@Test | ||
@DisplayName("존재하지 않지 않는 할일 조회시 TaskNotFound 예외 발생 테스트") | ||
void getTaskFail() { | ||
Assertions.assertThatThrownBy(() -> taskController.detail(100L)) | ||
.isInstanceOf(TaskNotFoundException.class); | ||
} | ||
|
||
@Test | ||
@DisplayName("할일 수정 후 해당 할일 값이 수정되었는지 테스트") | ||
void updateTaskTest() { | ||
// given | ||
Task task = taskController.create(createTask()); | ||
Task updateTask = new Task(); | ||
updateTask.setTitle("updated"); | ||
|
||
// when | ||
taskController.update(task.getId(), updateTask); | ||
|
||
// then | ||
Assertions.assertThat(taskController.detail(task.getId()).getTitle()).isEqualTo("updated"); | ||
} | ||
|
||
@Test | ||
@DisplayName("존재하지 않는 할일을 수정할 경우 TaskNotFound 예외 발생 테스트") | ||
void updatedFailTest() { | ||
// given | ||
Task task = taskController.create(createTask()); | ||
Task updateTask = new Task(); | ||
updateTask.setTitle("updated"); | ||
taskController.update(task.getId(), updateTask); | ||
// when then | ||
Assertions.assertThatThrownBy(() -> taskController.update(100L, updateTask)) | ||
.isInstanceOf(TaskNotFoundException.class); | ||
} | ||
|
||
@Test | ||
@DisplayName("1개 할일을 삭제 후 해당 리스트의 삭제후 리스트가 비어있는지 테스트") | ||
void deleteTaskSuccess() { | ||
// given | ||
Task task = taskController.create(createTask()); | ||
// when | ||
taskController.delete(task.getId()); | ||
// then | ||
Assertions.assertThat(taskController.list()).isEmpty(); | ||
} | ||
|
||
@Test | ||
@DisplayName("2개 할일을 삭제 후 해당 리스트의 삭제후 리스트크기가 감소했는 지 테스트") | ||
void deleteTasksSuccess() { | ||
// given | ||
taskController.create(createTask()); | ||
Task task = taskController.create(createTask()); | ||
int beforeSize = taskController.list().size(); | ||
// when | ||
taskController.delete(task.getId()); | ||
// then | ||
Assertions.assertThat(taskController.list().size()).isEqualTo(beforeSize - 1); | ||
} | ||
|
||
@Test | ||
@DisplayName("존재하지 않는 할일을 삭제할 경우 TaskNotFound 예외 발생 테스트") | ||
void deleteTaskFail() { | ||
Assertions.assertThatThrownBy(() -> taskController.delete(1L)).isInstanceOf(TaskNotFoundException.class); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package com.codesoom.assignment.controllers; | ||
|
||
import com.codesoom.assignment.TaskNotFoundException; | ||
import com.codesoom.assignment.application.TaskService; | ||
import com.codesoom.assignment.models.Task; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.mockito.BDDMockito; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; | ||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; | ||
import org.springframework.boot.test.context.SpringBootTest; | ||
import org.springframework.boot.test.mock.mockito.MockBean; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.test.web.client.match.MockRestRequestMatchers; | ||
import org.springframework.test.web.servlet.MockMvc; | ||
import org.springframework.test.web.servlet.ResultMatcher; | ||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; | ||
import org.springframework.test.web.servlet.result.MockMvcResultHandlers; | ||
import static org.hamcrest.Matchers.containsString; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import static org.mockito.BDDMockito.*; | ||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; | ||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; | ||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
|
||
@SpringBootTest | ||
@AutoConfigureMockMvc | ||
class TaskControllerWebTest { | ||
@Autowired | ||
private MockMvc mockMvc; | ||
|
||
@MockBean | ||
private TaskService taskService; | ||
|
||
@BeforeEach | ||
void setUp() { | ||
List<Task> tasks = new ArrayList<>(); | ||
Task task = new Task(); | ||
task.setTitle("test task"); | ||
tasks.add(task); | ||
|
||
given(taskService.getTasks()).willReturn(tasks); | ||
|
||
given(taskService.getTask(1L)).willReturn(task); | ||
|
||
given(taskService.getTask(100L)) | ||
.willThrow(new TaskNotFoundException(100L)); | ||
} | ||
@Test | ||
void list() throws Exception { | ||
mockMvc.perform(get("/tasks")) | ||
.andExpect((status().isOk())) | ||
.andExpect(content().string(containsString("test task"))); | ||
} | ||
|
||
@Test | ||
void detailWithValidId() throws Exception { | ||
mockMvc.perform(get("/tasks/1")) | ||
.andExpect(status().isOk()); | ||
} | ||
|
||
@Test | ||
void detailWithInvalidId() throws Exception { | ||
mockMvc.perform(get("/tasks/100")) | ||
.andExpect(status().isNotFound()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@SpringBootTest
는 스프링 부트 애플리케이션의 모든 빈을 로드하는 매우 포괄적인 테스트를 위한 어노테이션인데, 이로 인해 테스트 실행 시간이 길어질 수 있습니다. 또한, 모든 빈을 로드하는 것은 불필요한 의존성 문제를 일으킬 수 있습니다.만약 Mock을 이용해서 테스트를 진행하면 빈을 로드하지 않아도 될 수도 있고 혹은 통합 테스트를 작성해서 레이어 간의 동작하는 것을 테스트하고 싶으시다면
@DataJpaTest
를 사용할 수 있어요.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
최초에 빈으로 로드하는 방식으로 했다가
그냥 테스트마다 Service를 생성하는 방식으로 변경했는데
지우는걸 잊었던 것 같습니다
제거하겠습니다.