정현일
Title: 스토리에대한 제목을 간략하고 명확하게 작성
User Story
Scenario
def "zeroIfNotPresent #value가 null이면 0을 반환한다."() {
  given: "value는 null이다."
  def value = null
  when: "zeroIfNotPresent를 실행한다."
  def result = [
      OptionalUtil.zeroIfNotPresent((Integer)value),
      OptionalUtil.zeroIfNotPresent((Long)value),
      OptionalUtil.zeroIfNotPresent((Float)value),
      OptionalUtil.zeroIfNotPresent((Double)value)
  ]
  then: "#result는 0이다."
  result == [0, 0L, 0f, 0.0]
}def "falseIfNotPresent #value가 null이면 false를 반환하고 null이 아니면 #value를 반환한다."() {
  expect:
  result == OptionalUtil.falseIfNotPresent(value)
  where:
  value || result
  true  || true
  false || false
  null  || false
}def "throwIllegalArgumentExceptionIfNotPresent #value가 null이면 IllegalArgumentException을 throw한다."() {
  given: "value는 null이다."
  def value = null
  when: "throwIllegalArgumentExceptionIfNotPresent를 실행한다."
  OptionalUtil.throwIllegalArgumentExceptionIfNotPresent(value)
  then: "IllegalArgumentException이 발생한다."
  thrown(IllegalArgumentException)
}할일을 관리할 수 있는 시스템을 개발한다.
Todo Item을 등록/수정/삭제 할 수 있다.
Todo/Doing/Done 할 수 있고 Archive할 수 있어야 한다.
Todo Item을 등록할 때 Todo 상태로 시작한다.
상태변경을 할 수 있고 상태 변경은 
Todo > Doing, Doing > Done, Done > Doing, Doing > Todo로만 할 수 있다.
Archive는 Todo/Doing/Done 모든 상태에서 가능하다.
목록보기/상세보기 기능을 포함한다.
페이징 기능은 스펙에서 제외하고 현재 스펙에서는 전체 목록을 한번에 조회한다.
할일을 관리할 수 있는 시스템을 개발한다.
Todo Item을 등록/수정/삭제 할 수 있다.
Todo/Doing/Done 할 수 있고 Archive할 수 있어야 한다.
Todo Item을 등록할 때 Todo 상태로 시작한다.
상태변경을 할 수 있고 상태 변경은
Todo > Doing, Doing > Done, Done > Doing, Doing > Todo로만 할 수 있다.
Archive는 Todo/Doing/Done 모든 상태에서 가능하다.
목록보기/상세보기 기능을 포함한다.
페이징 기능은 스펙에서 제외하고 현재 스펙에서는 전체 목록을 한번에 조회한다.
Title: Todo Item의 상태를 변경한다.
User Story
Who: Todo Management System을 사용하는 사용자가
Why: Todo Item의 상태관리를 위해서
What: 각각의 Todo Item의 상태를 변경할 수 있다.
상태 변경은 Todo > Doing, Doing > Done,
Done > Doing, Doing > Todo로만 할 수 있다.
Scenario 1: Todo상태를 Doing상태로 변경하면 상태가 변경된다.
Scenario 2: Doing상태를 Done상태로 변경하면 상태가 변경된다.
Scenario 3: Done상태를 Doing상태로 변경하면 상태가 변경된다.
Scenario 4: Doing상태를 Todo상태로 변경하면 상태가 변경된다.
Scenario 5: Todo상태를 Done상태로 변경하면 상태가 변경되지 않고 예외사항이 발생한다.
Scenario 6: Done상태를 Todo상태로 변경하면 상태가 변경되지 않고 예외사항이 발생한다.
import kr.pe.nuti.home.api.core.application.Application;
import kr.pe.nuti.home.api.core.application.JpaConfiguration;
import kr.pe.nuti.home.api.core.application.WebConfiguration;
import kr.pe.nuti.home.api.domain.todo.TodoItem;
import kr.pe.nuti.home.api.enumeration.todo.TodoState;
import kr.pe.nuti.home.api.exception.todo.IllegalStateChangeException;
import kr.pe.nuti.home.api.repository.todo.TodoItemRepository;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Optional;
import static org.hamcrest.Matchers.is;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;/**
 * Title: Todo Item의 상태를 변경한다.
 * User Story:
 * Todo Management System을 사용하는 사용자가
 * Todo Item의 상태관리를 위해서
 * 각각의 Todo Item의 상태를 변경할 수 있다.
 * 상태 변경은 Todo > Doing, Doing > Done,
 * Done > Doing, Doing > Todo로만 할 수 있다.
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {JpaConfiguration.class, WebConfiguration.class, Application.class})
public class TodoServiceStateChangeTest {
  @Mock
  private TodoItemRepository todoItemRepository;
  @Autowired
  @Spy
  @InjectMocks
  private TodoService service;
  @Before
  public void setup() {
    MockitoAnnotations.initMocks(this);
  }
}/**
 * Todo상태를 Doing상태로 변경하면 상태가 변경된다.
 */
@Test
public void testStateChangeFromTodoToDoing() throws Exception {
  // given Todo 상태의 Todo Item
  TodoItem savedItem = new TodoItem();
  savedItem.setIdx(1L);
  savedItem.setState(TodoState.TODO);
  TodoItem changedItem = new TodoItem();
  changedItem.setIdx(1L);
  changedItem.setState(TodoState.DOING);
  when(todoItemRepository.findById(any(Long.class))).thenReturn(Optional.of(savedItem));
  when(todoItemRepository.save(any(TodoItem.class))).thenReturn(changedItem);
  TodoItem item = new TodoItem();
  item.setIdx(1L);
  // when Todo Item의 상태를 Doing으로 변경한다.
  TodoItem result = service.changeState(item, TodoState.DOING);
  // then Todo Item의 상태가 Doing으로 변경된다.
  Assert.assertThat(result.getState(), is(TodoState.DOING));
  verifyNoMoreInteractions(service);
}/**
 * Todo상태를 Done상태로 변경하면 상태가 변경되지 않고 예외사항이 발생한다.
 */
@Test(expected = IllegalStateChangeException.class)
public void testStateChangeFromTodoToDoneThrownException() throws Exception {
  try {
    // given Todo 상태의 Todo Item
    TodoItem savedItem = new TodoItem();
    savedItem.setIdx(1L);
    savedItem.setState(TodoState.TODO);
    TodoItem changedItem = new TodoItem();
    changedItem.setIdx(1L);
    changedItem.setState(TodoState.DOING);
    when(todoItemRepository.findById(any(Long.class))).thenReturn(Optional.of(savedItem));
    when(todoItemRepository.save(any(TodoItem.class))).thenReturn(changedItem);
    TodoItem item = new TodoItem();
    item.setIdx(1L);
    // when Todo Item의 상태를 Done으로 변경한다.
    service.changeState(item, TodoState.DONE);
    // then Todo Item의 상태가 변경되지 않고 예외사항이 발생한다.
  } catch (Exception e) {
    verifyNoMoreInteractions(service);
    throw e;
  }
}import kr.pe.nuti.home.api.domain.todo.TodoItem
import kr.pe.nuti.home.api.enumeration.todo.TodoState
import kr.pe.nuti.home.api.exception.todo.IllegalStateChangeException
import kr.pe.nuti.home.api.repository.todo.TodoItemRepository
import spock.lang.Issue
import spock.lang.Narrative
import spock.lang.See
import spock.lang.Specification
import spock.lang.Title@Title("Todo Item의 상태를 변경한다.")
@Narrative("""
Todo Management System을 사용하는 사용자가
Todo Item의 상태관리를 위해서
각각의 Todo Item의 상태를 변경할 수 있다.
상태 변경은 Todo > Doing, Doing > Done,
Done > Doing, Doing > Todo로만 할 수 있다.
""")
class TodoServiceStateChangeSpec extends Specification {
  TodoService service
  def todoItemRepository
  def setup() {
    todoItemRepository = Mock(TodoItemRepository)
    service = Spy(TodoService)
    service.todoItemRepository = todoItemRepository
  }
}@See(["https://github.com/hyeonil/smart-home-api/issues/6"])
@Issue("#6")
def "Todo상태를 Doing상태로 변경하면 상태가 변경된다."() {
  given: "Todo 상태의 Todo Item"
  TodoItem savedItem = new TodoItem([idx: 1L, state: TodoState.TODO])
  TodoItem changedItem = new TodoItem([idx: 1L, state: TodoState.DOING])
  todoItemRepository.findById(_) >> Optional.of(savedItem)
  todoItemRepository.save(_) >>  changedItem
  TodoItem item = new TodoItem([idx: 1L])
  when: "Todo Item의 상태를 Doing으로 변경한다."
  def result = service.changeState(item, TodoState.DOING)
  then: "Todo Item의 상태가 Doing으로 변경된다."
  1 * service.getItem(_)
  result.state == TodoState.DOING
}@See(["https://github.com/hyeonil/smart-home-api/issues/6"])
@Issue("#6")
def "Todo상태를 Done상태로 변경하면 상태가 변경되지 않고 예외사항이 발생한다."() {
  given: "Todo 상태의 Todo Item"
  TodoItem savedItem = new TodoItem([idx: 1L, state: TodoState.TODO])
  todoItemRepository.findById(_) >> Optional.of(savedItem)
  TodoItem item = new TodoItem([idx: 1L])
  when: "Todo Item의 상태를 Done으로 변경한다."
  service.changeState(item, TodoState.DONE)
  then: "Todo Item의 상태가 변경되지 않고 예외사항이 발생한다."
  1 * service.getItem(_)
  thrown(IllegalStateChangeException)
}